• File: FirstScan.php
  • Full Path: /home4/jdaxcom/j3dax.online/wp-content/plugins/wc-price-history/app/PriorPrice/FirstScan.php
  • Date Modified: 03/31/2026 11:49 AM
  • File size: 4.38 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

namespace PriorPrice;

use PriorPrice\Database\DbMigration;

/**
 * FirstScan class
 *
 * @since 2.0
 */
class FirstScan {

	public const SCAN_NOT_STARTED = 0;

	public const SCAN_IN_PROGRESS = 1;

	public const SCAN_FINISHED = 2;

	public const OPTION_NAME = 'first_history_scan';

	public const USER_OPTION_NAME = 'wc_price_history_first_scan_notice_closed';

	/**
	 * Settings data.
	 *
	 * @var SettingsData
	 */
	private $settings_data;

	/**
	 * History storage.
	 *
	 * @var HistoryStorage
	 */
	private $history_storage;

	/**
	 * FirstScan constructor.
	 *
	 * @since 2.0
	 *
	 * @param SettingsData   $settings_data   Settings data.
	 * @param HistoryStorage $history_storage History storage.
	 */
	public function __construct( SettingsData $settings_data, HistoryStorage $history_storage ) {

		$this->settings_data   = $settings_data;
		$this->history_storage = $history_storage;
	}

	/**
	 * Register hooks.
	 *
	 * @since 2.0
	 *
	 * @return void
	 */
	public function register_hooks(): void {

		add_action( 'admin_notices', [ $this, 'maybe_display_notice' ] );
		add_action( 'admin_init', [ $this, 'maybe_start_scan' ] );
	}

	/**
	 * Maybe display notice.
	 *
	 * @since 2.0
	 *
	 * @return void
	 */
	public function maybe_display_notice(): void {

		if ( self::SCAN_FINISHED === $this->settings_data->get_first_scan_status() ) {
			// check if user already closed this notice.
			if ( get_user_meta( get_current_user_id(), self::USER_OPTION_NAME, true ) ) {
				return;
			}

			?>
			<div class="notice notice-success is-dismissible" id="wc-price-history-first-scan-finished-notice">
				<h4>
					<?php esc_html_e( 'WC Price History', 'wc-price-history' );	?>
				</h4>
				<p>
					<?php
					esc_html_e( 'Success! WC Price History plugin has finished scanning products to set their initial price history.', 'wc-price-history' );
					?>
				</p>
				<p>
					<?php
					esc_html_e( 'You can now close this message and edit products as usual.', 'wc-price-history' );
					?>
				</p>
			</div>
			<?php

			return;
		}

		?>
		<div class="notice notice-info">
			<h4>
				<?php esc_html_e( 'WC Price History', 'wc-price-history' );	?>
			</h4>
			<p>
				<?php
				esc_html_e( 'WC Price History plugin is scanning products to set their initial price history.', 'wc-price-history' );
				?>
			</p>
			<p>
				<?php
				esc_html_e( 'While this process is going on, please refrain from editing products.', 'wc-price-history' );
				?>
			</p>
			<p>
				<?php
				esc_html_e( 'We will notify you when the scan is finished.', 'wc-price-history' );
				?>
			</p>
		</div>
		<?php
	}


	/**
	 * Maybe start scan.
	 *
	 * @since 2.0
	 *
	 * @return void
	 */
	public function maybe_start_scan(): void {

		if (
			isset( $_POST['action'] ) &&
			in_array( $_POST['action'], [ 'wc_price_history_force_first_scan_end', 'wc_price_history_restart_first_scan' ], true )
		) {
			return;
		}

		if ( self::SCAN_FINISHED === $this->settings_data->get_first_scan_status() ) {
			return;
		}

		$products = $this->get_products_without_history();

		if ( empty( $products ) ) {
			$this->settings_data->set_first_scan_status( self::SCAN_FINISHED );
			DbMigration::mark_completed_for_fresh_install();

			return;
		}

		$this->settings_data->set_first_scan_status( self::SCAN_IN_PROGRESS );
		DbMigration::mark_completed_for_fresh_install();

		foreach ( array_slice( $products, 0, 50 ) as $product ) {
			$this->history_storage->fill_empty_history( $product->ID, [] );
		}
	}

	public function force_end(): void {

		$this->settings_data->set_first_scan_status( self::SCAN_FINISHED );
	}

	public function restart(): void {

		$this->settings_data->set_first_scan_status( self::SCAN_NOT_STARTED );
	}

	/**
	 * Get products without history.
	 *
	 * @since 2.0
	 *
	 * @return array<\stdClass>
	 */
	private function get_products_without_history(): array {

		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT p.ID
				FROM {$wpdb->posts} p
				LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
				JOIN {$wpdb->postmeta} price_meta ON p.ID = price_meta.post_id AND price_meta.meta_key = %s AND CAST(price_meta.meta_value AS DECIMAL(10,2)) > 0
				WHERE p.post_type = %s
				AND p.post_status = %s
				AND pm.meta_id IS NULL",
				HistoryStorage::cf_key,
				'_price',
				'product',
				'publish'
			)
		);
	}
}