Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

jsarnowski / jsarnowski/jet-booking   php

Repository URL to install this package:

Version: 2.2.1 

/ db.php

<?php
namespace JET_ABAF;

/**
 * Database manager class
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Define DB class
 */
class DB {

	/**
	 * Check if booking DB table already exists
	 *
	 * @var bool
	 */
	private $bookings_table_exists = null;

	/**
	 * Check if units DB table already exists
	 *
	 * @var bool
	 */
	private $units_table_exists = null;

	/**
	 * Stores latest queried result to use it
	 *
	 * @var null
	 */
	public $latest_result = null;

	/**
	 * Stores latest inserted booking item
	 *
	 * @var array
	 */
	public $inserted_booking = false;

	/**
	 * 
	 */
	public $queried_booking = false;

	/**
	 * Constructor for the class
	 */
	public function __construct() {

		if ( ! empty( $_GET['jet_abaf_install_table'] ) ) {
			add_action( 'init', array( $this, 'install_table' ) );
		}

	}

	/**
	 * Check if booking table alredy exists
	 *
	 * @return boolean [description]
	 */
	public function is_bookings_table_exists() {

		if ( null !== $this->bookings_table_exists ) {
			return $this->bookings_table_exists;
		}

		$table = self::bookings_table();

		if ( $table === self::wpdb()->get_var( "SHOW TABLES LIKE '$table'" ) ) {
			$this->bookings_table_exists = true;
		} else {
			$this->bookings_table_exists = false;
		}

		return $this->bookings_table_exists;
	}

	/**
	 * Check if booking table alredy exists
	 *
	 * @return boolean [description]
	 */
	public function is_units_table_exists() {

		if ( null !== $this->units_table_exists ) {
			return $this->units_table_exists;
		}

		$table = self::units_table();

		if ( $table === self::wpdb()->get_var( "SHOW TABLES LIKE '$table'" ) ) {
			$this->units_table_exists = true;
		} else {
			$this->units_table_exists = false;
		}

		return $this->units_table_exists;

	}

	/**
	 * Check if all required DB tables are exists
	 *
	 * @return [type] [description]
	 */
	public function tables_exists() {
		return $this->is_bookings_table_exists() && $this->is_units_table_exists();
	}

	/**
	 * Try to recreate DB table by request
	 *
	 * @return void
	 */
	public function install_table() {

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$this->create_bookings_table();
		$this->create_units_table();

	}

	/**
	 * Returns WPDB instance
	 * @return [type] [description]
	 */
	public static function wpdb() {
		global $wpdb;
		return $wpdb;
	}

	/**
	 * Returns table name
	 * @return [type] [description]
	 */
	public static function bookings_table() {
		return self::wpdb()->prefix . 'jet_apartment_bookings';
	}

	/**
	 * Returns table name
	 * @return [type] [description]
	 */
	public static function units_table() {
		return self::wpdb()->prefix . 'jet_apartment_units';
	}

	/**
	 * Insert booking
	 *
	 * @param  array  $booking [description]
	 * @return [type]          [description]
	 */
	public function insert_booking( $booking = array() ) {

		$default_fields = array(
			'apartment_id',
			'apartment_unit',
			'check_in_date',
			'check_out_date',
		);

		$fields   = array_merge( $default_fields, $this->get_additional_db_columns() );
		$format   = array_fill( 0, count( $fields ), '%s' );
		$defaults = array_fill( 0, count( $fields ), '' );
		$defaults = array_combine( $fields, $defaults );
		$booking  = wp_parse_args( $booking, $defaults );

		$booking['check_in_date']  = $booking['check_in_date'] + 1;
		$booking['apartment_unit'] = $this->get_available_unit( $booking );

		if ( ! $this->is_booking_dates_available( $booking ) ) {
			return false;
		}

		$inserted = self::wpdb()->insert( self::bookings_table(), $booking, $format );

		if ( $inserted ) {
			$this->inserted_booking = $booking;
			return self::wpdb()->insert_id;
		} else {
			return false;
		}

	}

	/**
	 * Check if current booking dates is available
	 *
	 * @param  [type]  $booking [description]
	 * @return boolean          [description]
	 */
	public function is_booking_dates_available( $booking ) {

		$bookings_table = self::bookings_table();
		$apartment_id   = $booking['apartment_id'];
		$unit_id        = false;
		$from           = $booking['check_in_date'];
		$to             = $booking['check_out_date'];

		if ( ! empty( $booking['apartment_unit'] ) ) {
			$unit_id = $booking['apartment_unit'];
		}

		// Increase $from to 1 to avoid overlapping check-in and cak-out dates
		$from++;

		$query = "
			SELECT *
			FROM $bookings_table
			WHERE (
				( `check_in_date` >= $from AND `check_in_date` <= $to )
				OR ( `check_out_date` >= $from AND `check_out_date` <= $to )
				OR ( `check_in_date` < $from AND `check_out_date` >= $to )
			) AND `apartment_id` = $apartment_id";

		if ( $unit_id ) {
			$query .= " AND `apartment_unit` = $unit_id";
		}

		$query .= ";";

		$booked = self::wpdb()->get_results( $query, ARRAY_A );

		if ( empty( $booked ) ) {
			return true;
		} else {

			$skip_statuses   = Plugin::instance()->statuses->invalid_statuses();
			$skip_statuses[] = Plugin::instance()->statuses->temporary_status();

			foreach ( $booked as $index => $booking ) {
				if ( ! empty( $booking['status'] ) && in_array( $booking['status'], $skip_statuses ) ) {
					unset( $booked[ $index ] );
				}
			}

			if ( empty( $booked ) ) {
				return true;
			} else {
				return false;
			}

		}
	}

	/**
	 * Get availbale unit for passed dates
	 *
	 * @return [type]       [description]
	 */
	public function get_available_unit( $booking ) {

		$bookings_table = self::bookings_table();
		$units_table    = self::units_table();
		$apartment_id   = $booking['apartment_id'];
		$from           = $booking['check_in_date'];
		$to             = $booking['check_out_date'];

		$booked_units = self::wpdb()->get_results( "
			SELECT *
			FROM `{$bookings_table}`
			WHERE `apartment_id` = $apartment_id
			AND (
				( `check_in_date` >= $from AND `check_in_date` <= $to )
				OR ( `check_out_date` >= $from AND `check_out_date` <= $to )
				OR ( `check_in_date` < $from AND `check_out_date` >= $to )
			)
		", ARRAY_A );


		$all_units = $this->get_apartment_units( $apartment_id );

		if ( empty( $all_units ) ) {
			return null;
		}

		if ( empty( $booked_units ) ) {
			return $all_units[0]['unit_id'];
		}

		$skip_statuses   = Plugin::instance()->statuses->invalid_statuses();
		$skip_statuses[] = Plugin::instance()->statuses->temporary_status();

		foreach ( $all_units as $unit ) {

			$found = false;

			foreach ( $booked_units as $booked_unit ) {

				if ( ! isset( $booked_unit['status'] ) || ! in_array( $booked_unit['status'], $skip_statuses ) ) {
					if ( absint( $unit['unit_id'] ) === absint( $booked_unit['apartment_unit'] ) ) {
						$found = true;
					}
				}

			}

			if ( ! $found ) {
				return $unit['unit_id'];
			}

		}

		return null;

	}

	/**
	 * Update booking information in database
	 *
	 * @param  integer $booking_id [description]
	 * @param  array   $data       [description]
	 * @return [type]              [description]
	 */
	public function update_booking( $booking_id = 0, $data = array() ) {

		if ( ! empty( $data['check_in_date'] ) ) {
			$data['check_in_date']++;
		}

		self::wpdb()->update(
			self::bookings_table(),
			$data,
			array( 'booking_id' => $booking_id )
		);

	}

	/**
	 * Delete booking by passed parameters
	 *
	 * @param  [type] $where [description]
	 * @return [type]        [description]
	 */
	public function delete_booking( $where = array() ) {
		self::wpdb()->delete( self::bookings_table(), $where );
	}

	/**
	 * Delete unit by passed parameters
	 *
	 * @param  [type] $where [description]
	 * @return [type]        [description]
	 */
	public function delete_unit( $where = array() ) {
		self::wpdb()->delete( self::units_table(), $where );
	}

	/**
	 * Update unit
	 * @return [type] [description]
	 */
	public function update_unit( $unit_id, $data ) {
		self::wpdb()->update(
			self::units_table(),
			$data,
			array( 'unit_id' => $unit_id )
		);
	}

	/**
	 * Get future bookings for apartment ID (or all future bookings if apartment ID is not passed)
	 *
	 * @param  [type] $apartment_id [description]
	 * @return [type]               [description]
	 */
	public function get_future_bookings( $apartment_id = null ) {

		$table = self::bookings_table();
		$now   = time();
		$query = "SELECT * FROM $table WHERE `check_out_date` > $now";

		if ( $apartment_id ) {
			$apartment_id = absint( $apartment_id );
			$query       .= " AND `apartment_id` = $apartment_id";
		}

		$query .= ";";

		return self::wpdb()->get_results( $query, ARRAY_A );

	}

	/**
	 * Returns all available units for apartment
	 * @return [type] [description]
	 */
	public function get_apartment_units( $apartment_id ) {
		return $this->query(
			array(
				'apartment_id' => $apartment_id,
			),
			self::units_table()
		);
	}

	/**
	 * Returns all available units for apartment
	 * @return [type] [description]
	 */
	public function get_apartment_unit( $apartment_id, $unit_id ) {
		return $this->query(
			array(
				'apartment_id' => $apartment_id,
				'unit_id'      => $unit_id,
			),
			self::units_table()
		);
	}

	/**
	 * Returns appointment detail by order id
	 *
	 * @return [type] [description]
	 */
	public function get_booking_by( $field = 'booking_id', $value = null ) {

		$booking = $this->query(
			array( $field => $value ),
			self::bookings_table()
		);

		if ( empty( $booking ) ) {
			return false;
		}

		$booking = $booking[0];

		return $booking;

	}

	/**
	 * Get already booked apartments
	 *
	 * @param  [type] $from [description]
	 * @param  [type] $to   [description]
	 * @return [type]       [description]
	 */
	public function get_booked_apartments( $from, $to ) {

		$table       = self::bookings_table();
		$units_table = self::units_table();

		// Increase $from to 1 to avoid overlapping check-in and cak-out dates
		$from++;

		$booked = self::wpdb()->get_results( "
			SELECT apartment_id AS `apartment_id`, count( * ) AS `units`
			FROM $table
			WHERE `check_in_date` BETWEEN $from AND $to
			OR `check_out_date` BETWEEN $from AND $to
			OR ( `check_in_date` <= $from AND `check_out_date` >= $to )
			GROUP BY apartment_id;
		", ARRAY_A );

		if ( empty( $booked ) ) {
			return array();
		}

		$available = self::wpdb()->get_results( "
			SELECT apartment_id AS `apartment_id`, count( * ) AS `units`
			FROM $units_table
			GROUP BY apartment_id;
		", ARRAY_A );

		if ( ! empty( $available ) ) {
			$tmp = array();
			foreach ( $available as $row ) {
				$tmp[ $row['apartment_id'] ] = $row['units'];
			}
			$available = $tmp;
		} else {
			$available = array();
		}

		$result = array();

		foreach ( $booked as $apartment ) {

			$ap_id = $apartment['apartment_id'];

			if ( empty( $available[ $ap_id ] ) ) {
				$result[] = $apartment['apartment_id'];
			} else {

				$booked          = absint( $apartment['units'] );
				$available_units = absint( $available[ $ap_id ] );

				if ( $booked >= $available_units ) {
					$result[] = $apartment['apartment_id'];
				}

			}
		}

		return $result;

	}

	/**
	 * Check if is apartment id form updated booking record is available for new dates
	 *
	 * @return [type] [description]
	 */
	public function check_availability_on_update( $booking_id = 0, $apartment_id = 0, $from = 0, $to = 0 ) {

		$table = self::bookings_table();
		$units_table = self::units_table();

		// Increase $from to 1 to avoid overlapping check-in and cak-out dates
		$from++;

		$booked = self::wpdb()->get_results( "
			SELECT *
			FROM $table
			WHERE (
				`check_in_date` BETWEEN $from AND $to
				OR `check_out_date` BETWEEN $from AND $to
				OR ( `check_in_date` < $from AND `check_out_date` >= $to )
			) AND `apartment_id` = $apartment_id;
		", ARRAY_A );

		if ( empty( $booked ) ) {
			return true;
		}

		$this->latest_result = $booked;
		$booking_id          = absint( $booking_id );
		$count               = 0;

		foreach ( $booked as $booking ) {
			if ( absint( $booking['booking_id'] ) === $booking_id ) {
				continue;
			}

			$count++;

		}

		$available = self::wpdb()->get_results( "
			SELECT apartment_id AS `apartment_id`, count( * ) AS `units`
			FROM $units_table
			WHERE `apartment_id` = $apartment_id
			GROUP BY apartment_id;
		", ARRAY_A );

		if ( empty( $available ) && 0 < $count ) {
			return false;
		}

		if ( empty( $available ) && 0 === $count ) {
			return true;
		}

		if ( $count >= absint( $available[0]['units'] ) ) {
			return false;
		} else {
			return true;
		}

	}

	/**
	 * Returns additional DB fields
	 *
	 * @return [type] [description]
	 */
	public function get_additional_db_columns() {
		return apply_filters( 'jet-abaf/db/additional-db-columns', array() );
	}

	/**
	 * Create database table for tracked information
	 *
	 * @return void
	 */
	public function create_bookings_table( $delete_if_exists = false ) {

		if ( ! function_exists( 'dbDelta' ) ) {
			require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
		}

		$table = self::bookings_table();

		if ( $delete_if_exists && $this->is_bookings_table_exists() ) {
			self::wpdb()->query( "DROP TABLE $table;" );
		}

		$charset_collate    = self::wpdb()->get_charset_collate();
		$columns_schema     = 'booking_id bigint(20) NOT NULL AUTO_INCREMENT,';
		$columns_schema    .= 'status text,';
		$columns_schema    .= 'apartment_id bigint(20),';
		$columns_schema    .= 'apartment_unit bigint(20),';
		$additional_columns = $this->get_additional_db_columns();
		$additional_columns = array_unique( $additional_columns );

		if ( is_array( $additional_columns ) && ! empty( $additional_columns ) ) {
			foreach ( $additional_columns as $column ) {
				$columns_schema .= $column . ' text,';
			}
		}

		$columns_schema .= 'check_in_date bigint(20),';
		$columns_schema .= 'check_out_date bigint(20),';
		$columns_schema .= 'import_id text,';

		$sql = "CREATE TABLE $table (
			$columns_schema
			PRIMARY KEY (booking_id)
		) $charset_collate;";

		dbDelta( $sql );

	}

	/**
	 * Create DB table for apartment units
	 *
	 * @return [type] [description]
	 */
	public function create_units_table( $delete_if_exists = false ) {

		if ( ! function_exists( 'dbDelta' ) ) {
			require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
		}

		$table = self::units_table();

		if ( $delete_if_exists && $this->is_units_table_exists() ) {
			self::wpdb()->query( "DROP TABLE $table;" );
		}

		$charset_collate = self::wpdb()->get_charset_collate();

		$sql = "CREATE TABLE $table (
			unit_id bigint(20) NOT NULL AUTO_INCREMENT,
			apartment_id bigint(20),
			unit_title text,
			notes text,
			PRIMARY KEY (unit_id)
		) $charset_collate;";

		dbDelta( $sql );

	}

	/**
	 * Insert new columns into existing bookings table
	 *
	 * @param  [type] $columns [description]
	 * @return [type]          [description]
	 */
	public function insert_table_columns( $columns = array() ) {

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$table          = self::bookings_table();
		$columns_schema = '';

		foreach ( $columns as $column ) {
			$columns_schema .= 'ADD ' . $column . ' text, ';
		}

		$columns_schema = rtrim( $columns_schema, ', ' );

		$sql = "ALTER TABLE $table
			$columns_schema;";

		self::wpdb()->query( $sql );

	}

	/**
	 * Delete columns into existing bookings table
	 *
	 * @param  [type] $columns [description]
	 * @return [type]          [description]
	 */
	public function delete_table_columns( $columns ) {

		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$table          = self::bookings_table();
		$columns_schema = '';

		foreach ( $columns as $column ) {
			$columns_schema .= 'DROP COLUMN ' . $column . ', ';
		}

		$columns_schema = rtrim( $columns_schema, ', ' );

		$sql = "ALTER TABLE $table
			$columns_schema;";

		self::wpdb()->query( $sql );

	}

	/**
	 * Returns default DB fields list
	 * @return [type] [description]
	 */
	public function get_default_fields() {
		return array(
			'booking_id',
			'status',
			'apartment_id',
			'apartment_unit',
			'check_in_date',
			'check_out_date',
		);
	}

	/**
	 * Update database with new columns
	 *
	 * @param  [type] $new_columns [description]
	 * @return [type]              [description]
	 */
	public function update_columns_diff( $new_columns = array() ) {

		$table           = self::bookings_table();
		$default_columns = $this->get_default_fields();

		$columns          = self::wpdb()->get_results( "SHOW COLUMNS FROM $table", ARRAY_A );
		$existing_columns = array();

		if ( empty( $columns ) ) {
			return false;
		}

		foreach ( $columns as $column ) {
			if ( ! in_array( $column['Field'], $default_columns ) ) {
				$existing_columns[] = $column['Field'];
			}
		}

		if ( empty( $new_columns ) && empty( $existing_columns ) ) {
			return;
		}

		$to_delete = array_diff( $existing_columns, $new_columns );
		$to_add    = array_diff( $new_columns, $existing_columns );

		if ( ! empty( $to_delete ) ) {
			$this->delete_table_columns( $to_delete );
		}

		if ( ! empty( $to_add ) ) {
			$this->insert_table_columns( $to_add );
		}

	}

	/**
	 * Add nested query arguments
	 *
	 * @param  [type]  $key    [description]
	 * @param  [type]  $value  [description]
	 * @param  boolean $format [description]
	 * @return [type]          [description]
	 */
	public function get_sub_query( $key, $value, $format = false ) {

		$query = '';
		$glue  = '';

		if ( ! $format ) {

			if ( false !== strpos( $key, '!' ) ) {
				$format = '`%1$s` != \'%2$s\'';
				$key    = ltrim( $key, '!' );
			} else {
				$format = '`%1$s` = \'%2$s\'';
			}

		}

		foreach ( $value as $child ) {
			$query .= $glue;
			$query .= sprintf( $format, esc_sql( $key ), esc_sql( $child ) );
			$glue   = ' OR ';
		}

		return $query;

	}

	/**
	 * Add where arguments to query
	 *
	 * @param array  $args [description]
	 * @param string $rel  [description]
	 */
	public function add_where_args( $args = array(), $rel = 'AND' ) {

		$query      = '';
		$multi_args = false;

		if ( ! empty( $args ) ) {

			$query  .= ' WHERE ';
			$glue    = '';
			$search  = array();
			$props   = array();

			if ( count( $args ) > 1 ) {
				$multi_args = true;
			}

			foreach ( $args as $key => $value ) {

				$format = '`%1$s` = \'%2$s\'';

				$query .= $glue;

				if ( false !== strpos( $key, '!' ) ) {
					$key    = ltrim( $key, '!' );
					$format = '`%1$s` != \'%2$s\'';
				} elseif ( false !== strpos( $key, '>' ) ) {
					$key    = rtrim( $key, '>' );
					$format = '`%1$s` > %2$d';
				} elseif ( false !== strpos( $key, '<' ) ) {
					$key    = rtrim( $key, '<' );
					$format = '`%1$s` < %2$d';
				}

				if ( is_array( $value ) ) {
					$query .= sprintf( '( %s )', $this->get_sub_query( $key, $value, $format ) );
				} else {
					$query .= sprintf( $format, esc_sql( $key ), esc_sql( $value ) );
				}

				$glue = ' ' . $rel . ' ';

			}

		}

		return $query;

	}

	/**
	 * Check if booking DB column is exists
	 *
	 * @return [type] [description]
	 */
	public function column_exists( $column ) {

		$table = self::bookings_table();
		return self::wpdb()->query( "SHOW COLUMNS FROM `$table` LIKE '$column'" );

	}

	/**
	 * Add order arguments to query
	 *
	 * @param array $args [description]
	 */
	public function add_order_args( $order = array() ) {

		$query = '';

		if ( ! empty( $order['orderby'] ) ) {

			$orderby = $order['orderby'];
			$order   = ! empty( $order['order'] ) ? $order['order'] : 'desc';
			$order   = strtoupper( $order );
			$query  .= " ORDER BY $orderby $order";

		}

		return $query;

	}

	/**
	 * Return count of queried items
	 *
	 * @return [type] [description]
	 */
	public function count( $args = array(), $rel = 'AND' ) {

		$table = self::bookings_table();

		$query = "SELECT count(*) FROM $table";

		if ( ! $rel ) {
			$rel = 'AND';
		}

		if ( isset( $args['after'] ) ) {
			$after = $args['after'];
			unset( $args['after'] );
			$args['ID>'] = $after;
		}

		if ( isset( $args['before'] ) ) {
			$before = $args['before'];
			unset( $args['before'] );
			$args['ID<'] = $before;
		}

		$query .= $this->add_where_args( $args, $rel );

		return self::wpdb()->get_var( $query );

	}

	/**
	 * Check if booking already exists
	 *
	 * @param  string $by_field [description]
	 * @param  [type] $value    [description]
	 * @return [type]           [description]
	 */
	public function booking_exists( $by_field = 'ID', $value = null ) {
		$count = $this->count( array( $by_field => $value ) );
		return ! empty( $count );
	}

	/**
	 * Query data from db table
	 *
	 * @return [type] [description]
	 */
	public function query( $args = array(), $table = null, $limit = 0, $offset = 0, $order = array(), $rel = 'AND' ) {

		if ( ! $table ) {
			$table = self::bookings_table();
		}

		$query = "SELECT * FROM $table";

		if ( ! $rel ) {
			$rel = 'AND';
		}

		if ( isset( $args['after'] ) ) {
			$after = $args['after'];
			unset( $args['after'] );
			$args['ID>'] = $after;
		}

		if ( isset( $args['before'] ) ) {
			$before = $args['before'];
			unset( $args['before'] );
			$args['ID<'] = $before;
		}

		$query .= $this->add_where_args( $args, $rel );
		$query .= $this->add_order_args( $order );

		if ( intval( $limit ) > 0 ) {
			$limit  = absint( $limit );
			$offset = absint( $offset );
			$query .= " LIMIT $offset, $limit";
		}

		$raw = self::wpdb()->get_results( $query, ARRAY_A );

		return $raw;

	}

}