File: /var/www/html/ielts-store/wp-content/plugins/woocommerce-zapier/src/TaskHistory/TaskDataStore.php
<?php
namespace OM4\WooCommerceZapier\TaskHistory;
use OM4\WooCommerceZapier\Exception\InvalidTaskException;
use OM4\WooCommerceZapier\Helper\WordPressDB;
use WC_Data;
use WC_Object_Data_Store_Interface;
defined( 'ABSPATH' ) || exit;
/**
 * Task Data Store.
 *
 * Responsible for creating, reading, updating and deleting Task objects and records
 * to and from the custom database table.
 *
 * @since 2.0.0
 */
class TaskDataStore implements WC_Object_Data_Store_Interface {
	/**
	 * Number of days to retain task history records for.
	 *
	 * Any records older than this are automatically deleted each day.
	 */
	const TASK_RETENTION_DAYS = '30';
	/**
	 * Database table name for storing task history records.
	 */
	const TASK_HISTORY_TABLE = 'wc_zapier_history';
	/**
	 * WordPressDB instance.
	 *
	 * @var WordPressDB
	 */
	protected $wp_db;
	/**
	 * Constructor
	 *
	 * @param WordPressDB $wpdb WordPressDB instance.
	 */
	public function __construct( WordPressDB $wpdb ) {
		$this->wp_db = $wpdb;
	}
	/**
	 * Get the database table name where Zapier Tasks are stored.
	 *
	 * @return string
	 */
	public function get_table_name() {
		return $this->wp_db->prefix . self::TASK_HISTORY_TABLE;
	}
	/**
	 * Create a new Task record.
	 *
	 * @param Task $task Task object.
	 *
	 * @return void
	 */
	public function create( &$task ) {
		$this->wp_db->insert(
			$this->get_table_name(),
			$this->map_to_db_fields_for_edit( $task ),
			$this->get_db_field_formats()
		);
		$task_id = $this->wp_db->insert_id;
		$task->set_id( $task_id );
		$task->apply_changes();
	}
	/**
	 * Method to read a task record from the database
	 *
	 * @param Task $task Task object.
	 *
	 * @throws InvalidTaskException If the specified task doesn't exist.
	 *
	 * @return void
	 */
	public function read( &$task ) {
		$task->set_defaults();
		if ( 0 === $task->get_id() ) {
			// TODO: log?
			throw new InvalidTaskException( __( 'Invalid task history: no ID.', 'woocommerce-zapier' ) );
		}
		$data = $this->wp_db->get_row(
			$this->wp_db->prepare(
				'SELECT * FROM ' . $this->get_table_name() . ' WHERE history_id = %d LIMIT 1;',
				$task->get_id()
			),
			ARRAY_A
		);
		if ( ! is_array( $data ) ) {
			// TODO: log?
			throw new InvalidTaskException( __( 'Invalid task history record: not found.', 'woocommerce-zapier' ) );
		}
		$task->set_props(
			array(
				'id'            => $data['history_id'],
				'date_time'     => '0000-00-00 00:00:00' === $data['date_time'] ? null : $data['date_time'],
				'webhook_id'    => $data['webhook_id'],
				'resource_type' => $data['resource_type'],
				'resource_id'   => $data['resource_id'],
				'message'       => $data['message'],
				'type'          => $data['type'],
			)
		);
		$task->set_object_read( true );
	}
	/**
	 * Updates a record in the database.
	 *
	 * @param Task $task Task object.
	 *
	 * @return void
	 */
	public function update( &$task ) {
		$data = $this->map_to_db_fields_for_edit( $task );
		$this->wp_db->update(
			$this->get_table_name(),
			$data,
			array(
				'history_id' => $task->get_id(),
			),
			$this->get_db_field_formats(),
			'%d'
		);
		$task->apply_changes();
	}
	/**
	 * Deletes a record from the database.
	 *
	 * @param Task  $task Data object.
	 * @param array $args Array of args to pass to the delete method.
	 *
	 * @return bool result
	 */
	public function delete( &$task, $args = array() ) {
		$result = $this->wp_db->delete(
			$this->get_table_name(),
			array(
				'history_id' => $task->get_id(),
			),
			'%d'
		);
		return ( false !== $result ) ? true : false;
	}
	/**
	 * Map a Task object to an array that represents a database row.
	 *
	 * Excludes the primary key.
	 *
	 * @param Task $task The Task record.
	 *
	 * @return array
	 */
	protected function map_to_db_fields_for_edit( $task ) {
		$data = array(
			'date_time'     => $task->get_date_time( 'edit' ),
			'webhook_id'    => $task->get_webhook_id( 'edit' ),
			'resource_type' => $task->get_resource_type( 'edit' ),
			'resource_id'   => $task->get_resource_id( 'edit' ),
			'message'       => $task->get_message( 'edit' ),
			'type'          => $task->get_type( 'edit' ),
		);
		// Convert WC_DateTime to a MySQL date/time.
		$data['date_time'] = $data['date_time']->date( 'Y-m-d H:i:s' );
		return $data;
	}
	/**
	 * Get the total number of task records matching the specified criteria.
	 *
	 * @param array $args Search criteria.
	 *
	 * @return int Number of tasks matching the search criteria.
	 */
	public function get_tasks_count( $args = array() ) {
		return absint( $this->get_tasks_matching_criteria( array_merge( array( 'count' => true ), $args ) ) );
	}
	/**
	 * Get Task records matching the specified criteria.
	 *
	 * @param array $args Search criteria.
	 *
	 * @return Task[]
	 */
	public function get_tasks( $args = array() ) {
		$tasks = $this->get_tasks_matching_criteria( $args );
		if ( ! is_array( $tasks ) ) {
			return array();
		}
		return $tasks;
	}
	/**
	 * Get Task records matching the specified criteria.
	 *
	 * @param array $args Search criteria.
	 *
	 * @return Task[]|int
	 */
	protected function get_tasks_matching_criteria( $args = array() ) {
		$default_args = array(
			'resource_id'   => null,
			'resource_type' => null,
			'orderby'       => 'history_id',
			'order'         => 'DESC',
			'limit'         => 20,
			'offset'        => 0,
			'count'         => false,
		);
		$args = wp_parse_args( $args, $default_args );
		$query = 'SELECT * FROM ' . $this->get_table_name();
		if ( ! empty( $args['resource_type'] ) && ! empty( $args['resource_id'] ) ) {
			$query .= $this->wp_db->prepare( ' WHERE resource_id = %d AND resource_type = %s', absint( $args['resource_id'] ), $args['resource_type'] );
		}
		if ( true === $args['count'] ) {
			$query_count = str_replace( 'SELECT * FROM', 'SELECT count(history_id) FROM', $query );
			return absint( $this->wp_db->get_var( $query_count ) );
		}
		// Sanity checks.
		if ( $args['limit'] > 200 ) {
			$args['limit'] = $default_args['limit'];
		}
		$query .= $this->wp_db->prepare( ' ORDER BY ' . sanitize_sql_orderby( $args['orderby'] . ' ' . $args['order'] ) . ' LIMIT %d, %d', absint( $args['offset'] ), absint( $args['limit'] ) );
		$items = array();
		foreach ( $this->wp_db->get_results( $query, ARRAY_A ) as $item ) {
			$items[] = new Task( $this, $item );
		}
		return $items;
	}
	/*
	|--------------------------------------------------------------------------
	| Helpers
	|--------------------------------------------------------------------------
	*/
	/**
	 * Factory for creating new task.
	 *
	 * @param int|Task|array $task Task ID to load from the DB (optional) or already queried data.
	 * @return Task
	 */
	public function new_task( $task = 0 ) {
		return new Task( $this, $task );
	}
	/**
	 * Get the database field formats for each field
	 *
	 * Excludes the primary key.
	 *
	 * @return array
	 */
	protected function get_db_field_formats() {
		return array(
			'%s',
			'%d',
			'%s',
			'%d',
			'%s',
			'%s',
		);
	}
	/**
	 * Deletes Task records older than 30 days.
	 *
	 * Executed daily as part of the `wc_zapier_history_cleanup` scheduled job.
	 *
	 * @return void
	 */
	public function cleanup_old_tasks() {
		$retention = '-' . self::TASK_RETENTION_DAYS . 'days';
		$timestamp = strtotime( $retention );
		if ( ! $timestamp ) {
			return;
		}
		$this->wp_db->query(
			$this->wp_db->prepare( 'DELETE FROM ' . $this->get_table_name() . ' WHERE date_time < %s', gmdate( 'Y-m-d H:i:s', $timestamp ) )
		);
	}
	/**
	 * Get the total number of Trigger-related task records for each webhook ID.
	 *
	 * These counts are for trigger-related tasks only.
	 *
	 * @return array Associative array with `webhook_id`, `resource_type` and `count` values.
	 */
	public function get_trigger_task_count() {
		$results = $this->wp_db->get_results(
			'SELECT webhook_id, resource_type, count(*) AS count FROM ' . $this->get_table_name() . " WHERE type='trigger' GROUP BY webhook_id, resource_type ORDER BY webhook_id DESC",
			ARRAY_A
		);
		if ( ! is_array( $results ) ) {
			return array();
		}
		foreach ( $results as $key => $result ) {
			$results[ $key ]['count']      = intval( $results[ $key ]['count'] );
			$results[ $key ]['webhook_id'] = intval( $results[ $key ]['webhook_id'] );
		}
		return $results;
	}
	/**
	 * Get the total number of Action-related task records for each resource.
	 *
	 * These counts are for action-related tasks only.
	 *
	 * @return array Associative array with `resource_type` and `count` values.
	 */
	public function get_action_task_counts() {
		$results = $this->wp_db->get_results(
			'SELECT resource_type, count(*) AS count FROM ' . $this->get_table_name() . " WHERE type='action' GROUP BY resource_type ORDER BY resource_type ASC",
			ARRAY_A
		);
		if ( ! is_array( $results ) ) {
			return array();
		}
		foreach ( $results as $key => $result ) {
			$results[ $key ]['count'] = intval( $results[ $key ]['count'] );
		}
		return $results;
	}
	/*
	|--------------------------------------------------------------------------
	| Unused methods (required by WC_Object_Data_Store_Interface but not used)
	|--------------------------------------------------------------------------
	*/
	/**
	 * Returns an array of meta for an object.
	 *
	 * @internal Do not use.
	 * @codeCoverageIgnore
	 *
	 * @param WC_Data $data Data object.
	 *
	 * @return array
	 */
	public function read_meta( &$data ) {
		return array();
	}
	/**
	 * Deletes meta based on meta ID.
	 *
	 * @internal Do not use.
	 * @codeCoverageIgnore
	 *
	 * @param WC_Data $data Data object.
	 * @param object  $meta Meta object (containing at least ->id).
	 *
	 * @return array
	 */
	public function delete_meta( &$data, $meta ) {
		return array();
	}
	/**
	 * Add new piece of meta.
	 *
	 * @internal Do not use.
	 * @codeCoverageIgnore
	 *
	 * @param WC_Data $data Data object.
	 * @param object  $meta Meta object (containing ->key and ->value).
	 *
	 * @return int meta ID
	 */
	public function add_meta( &$data, $meta ) {
		return 0;
	}
	/**
	 * Update meta.
	 *
	 * @internal Do not use.
	 * @codeCoverageIgnore
	 *
	 * @param WC_Data $data Data object.
	 * @param object  $meta Meta object (containing ->id, ->key and ->value).
	 *
	 * @return void
	 */
	public function update_meta( &$data, $meta ) {
	}
}