Schedule showing and hiding of WooCommerce products

PHOTO EMBED

Sun Mar 30 2025 19:10:53 GMT+0000 (Coordinated Universal Time)

Saved by @satinbest #php

<?php
/*
Plugin Name: WooCommerce - Schedule showing and hiding of products
Plugin URI: https://www.damiencarbery.com/2025/01/schedule-showing-and-hiding-of-woocommerce-products/
Description: Add the ability to show and hide a product on specified dates.
Author: Damien Carbery
Author URI: https://www.damiencarbery.com
Version: 0.1.20250108
Requires Plugins: woocommerce
*/


class ScheduleHideProducts {
	private $show_hide_dates_start_key;
	private $show_hide_dates_end_key;


	// Returns an instance of this class. 
	public static function get_instance() {
		if ( null == self::$instance ) {
			self::$instance = new self;
		} 
		return self::$instance;
	}


	// Initialize the plugin variables.
	public function __construct() {
		// The custom meta key for the start and end dates.
		$this->show_hide_dates_start_key = '_show_hide_dates_start';
		$this->show_hide_dates_end_key = '_show_hide_dates_end';

		$this->init();
	}


	// Set up WordPress specfic actions.
	public function init() {
		// Declare that this plugin supports WooCommerce HPOS.
		// This plugin does not interact with WooCommerce orders so it doesn't have to do anything special to support HPOS.
		add_action( 'before_woocommerce_init', function() {
			if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
			\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
			}
		} );

		// Add the date fields to the Product/General tab.
		add_action( 'woocommerce_product_options_pricing', array( $this, 'add_hide_date' ) );
		// Save the date fields values if set.
		add_action( 'woocommerce_process_product_meta', array( $this, 'save_show_hide_dates' ), 10, 2 );
		// Scheduled check for products to show or hide.
		add_action( 'woocommerce_scheduled_sales', array( $this, 'scheduled_show_hide_check' ) );

		// For debugging.
		add_shortcode( 'show_hide', array( $this, 'show_hide_shortcode' ) );
	}


	public function show_hide_shortcode() {
		$product_id = 32;

		$visibility_terms = $this->get_visibility_terms( $product_id );
		echo '<pre>', var_export( $visibility_terms, true ), '</pre>';
		
		array_push( $visibility_terms, 'exclude-from-catalog', 'exclude-from-search' );
		echo '<pre>', var_export( $visibility_terms, true ), '</pre>';
	}

	// Get the terms without the 'exclude-from-catalog' or 'exclude-from-search' terms.
	private function get_visibility_terms( $product_id ) {
		// Get the term slugs.
		$visibility_terms = wp_list_pluck( wp_get_object_terms( $product_id, 'product_visibility' ), 'slug' );
		
		// Remove 'exclude-from-catalog' or 'exclude-from-search' terms, if present.
		$visibility_terms = array_filter( $visibility_terms, function( $x ) { return $x != 'exclude-from-catalog' && $x != 'exclude-from-search'; } );
		
		return $visibility_terms;
	}


	// Add the date fields to the Product/General tab.
	public function add_hide_date() {
		$post_id = get_the_ID();
		$product_type = WC_Product_Factory::get_product_type( $post_id );
		$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' );
		$product = new $classname( $post_id );

		$show_hide_dates_start_timestamp = $product->get_meta( $this->show_hide_dates_start_key, true, 'edit' );
		$show_hide_dates_end_timestamp = $product->get_meta( $this->show_hide_dates_end_key, true, 'edit' );

		// Convert the timestamp into Y-m-d format.
		$show_hide_dates_start = $show_hide_dates_start_timestamp ? date_i18n( 'Y-m-d', $show_hide_dates_start_timestamp ) : '';
		$show_hide_dates_end = $show_hide_dates_end_timestamp ? date_i18n( 'Y-m-d', $show_hide_dates_end_timestamp ) : '';

		// Attach DatePicker to the two fields.
		echo "
		<script>
		jQuery( function ( $ ) {
			$( '.show_hide_dates_fields' ).each( function () {
				$( this )
					.find( 'input' )
					.datepicker( {
						defaultDate: '',
						dateFormat: 'yy-mm-dd',
						numberOfMonths: 1,
						showButtonPanel: true,
					} );
			} );
			$( '#woocommerce-product-data' ).on(
				'click',
				'.cancel_show_hide_schedule',
				function () {
					var wrap = $( this ).closest( 'div, table' );

					//$( this ).hide();  // Hide the 'Cancel' link.
					wrap.find( '.show_hide_dates_fields' ).find( 'input' ).val( '' );

					return false;
				}
			);
		} );
		</script>
		";
		echo '
		<style>
		.woocommerce_options_panel .show_hide_dates_fields .short:first-of-type { margin-bottom: 1em; }
		.woocommerce_options_panel .show_hide_dates_fields .short:nth-of-type(2) { clear: left; }
		</style>
		<p class="form-field show_hide_dates_fields">
			<label for="'.$this->show_hide_dates_start_key.'">' . esc_html__( 'Show/hide dates', 'woocommerce' ) . '</label>
			<input type="text" class="short" name="'.$this->show_hide_dates_start_key.'" id="'.$this->show_hide_dates_start_key.'" value="' . esc_attr( $show_hide_dates_start ) . '" placeholder="' . esc_html( _x( 'Starting&hellip;', 'placeholder', 'woocommerce' ) ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" />
			<input type="text" class="short" name="'.$this->show_hide_dates_end_key.'" id="'.$this->show_hide_dates_end_key.'" value="' . esc_attr( $show_hide_dates_end ) . '" placeholder="' . esc_html( _x( 'Ending&hellip;', 'placeholder', 'woocommerce' ) ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" />' .
			'<a href="#" class="description cancel_show_hide_schedule">' . esc_html__( 'Cancel', 'woocommerce' ) . '</a>' . wc_help_tip( __( 'The product will be shown at 00:00:00 of "Starting" date and hidden at 23:59:59 of "Ending" date.', 'woocommerce' ) ) . '
		</p>';
	}


	// Save the date fields values if set.
	public function save_show_hide_dates( $post_id, $post ) {
		$product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( wp_unslash( $_POST['product-type'] ) );
		$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' );
		$product = new $classname( $post_id );

		// Handle show/hide dates.
		$show_hide_dates_start = '';
		$show_hide_dates_end   = '';

		// Force 'date from' to beginning of day.
		if ( isset( $_POST['_show_hide_dates_start'] ) ) {
			$show_hide_dates_start = wc_clean( wp_unslash( $_POST['_show_hide_dates_start'] ) );

			if ( ! empty( $show_hide_dates_start ) ) {
				// ToDo: Maybe use code from set_date_prop() in woocommerce/includes/abstracts/abstract-wc-data.php.
				$show_hide_dates_start = new WC_DateTime( date( 'Y-m-d 00:00:00', strtotime( $show_hide_dates_start ) ), new DateTimeZone( 'UTC' ) );

				// ToDo: If $show_hide_dates_start is in the future I should hide the product now! (set product_visibility terms)?
			}
		}

		// Force 'date to' to the end of the day.
		if ( isset( $_POST['_show_hide_dates_end'] ) ) {
			$show_hide_dates_end = wc_clean( wp_unslash( $_POST['_show_hide_dates_end'] ) );

			if ( ! empty( $show_hide_dates_end ) ) {
				// ToDo: Maybe use code from set_date_prop() in woocommerce/includes/abstracts/abstract-wc-data.php.
				$show_hide_dates_end = new WC_DateTime( date( 'Y-m-d 00:00:00', strtotime( $show_hide_dates_end ) ), new DateTimeZone( 'UTC' ) );
			}
		}

		$product->update_meta_data( $this->show_hide_dates_start_key, $show_hide_dates_start ? $show_hide_dates_start->getTimestamp() : $show_hide_dates_start );
		$product->update_meta_data( $this->show_hide_dates_end_key, $show_hide_dates_end ? $show_hide_dates_end->getTimestamp() : $show_hide_dates_end );
		$product->save();
	}


	// Returns an array of IDs of products that will be shown soon.
	public function get_starting_shows() {
		global $wpdb;

		return $wpdb->get_col(
			$wpdb->prepare(
				"SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta
				WHERE postmeta.meta_key = '%s'
					AND postmeta.meta_value > 0
					AND postmeta.meta_value < %s",
				$this->show_hide_dates_start_key, time()
			)
		);
	}


	// Returns an array of IDs of products that are due to be hidden.
	public function get_ending_shows() {
		global $wpdb;

		return $wpdb->get_col(
			$wpdb->prepare(
				"SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta
				WHERE postmeta.meta_key = '%s'
					AND postmeta.meta_value > 0
					AND postmeta.meta_value < %s",
				$this->show_hide_dates_end_key, time()
			)
		);
	}


	// Scheduled check for products to show or hide.
	public function scheduled_show_hide_check() {
		// Get products to be shown (or published).
		$product_ids = $this->get_starting_shows();

		if ( $product_ids ) {
			$visibility_ids = wc_get_product_visibility_term_ids();

			foreach ( $product_ids as $product_id ) {
				$product = wc_get_product( $product_id );

				if ( $product ) {
					$visibility_terms = $this->get_visibility_terms( $product_id );

					wp_set_object_terms( $product_id, $visibility_terms, 'product_visibility' );

					// Delete the start date so we don't keep changing the product_visibility terms.
					$product->delete_meta_data( $this->show_hide_dates_start_key );
					$product->save();
				}
			}
		}


		// Get products to be hidden (or unpublished).
		$product_ids = $this->get_ending_shows();

		if ( $product_ids ) {
			foreach ( $product_ids as $product_id ) {
				$product = wc_get_product( $product_id );

				if ( $product ) {
					// Get the terms without the 'exclude-from-catalog' or 'exclude-from-search' terms.
					$visibility_terms = $this->get_visibility_terms( $product_id );
					// Add 'exclude-from-catalog' and 'exclude-from-search' terms so that the product will be hidden.
					array_push( $visibility_terms, 'exclude-from-catalog', 'exclude-from-search' );

					// Hide product by adding 'exclude-from-catalog' and 'exclude-from-search' terms.
					wp_set_object_terms( $product_id, $visibility_terms, 'product_visibility' );

					// Delete the end date so we don't keep hiding this product.
					$product->delete_meta_data( $this->show_hide_dates_end_key );
					$product->save();
				}
			}
		}
	}
}

$ScheduleHideProducts = new ScheduleHideProducts();
content_copyCOPY