ted' => $this->get_download_permissions_granted( $order ),
'new_order_email_sent' => $this->get_email_sent( $order ),
'recorded_sales' => $this->get_recorded_sales( $order ),
'recorded_coupon_usage_counts' => $this->get_recorded_coupon_usage_counts( $order ),
)
),
$this->operational_data_column_mapping
);
if ( $row ) {
$db_updates[] = array_merge(
array(
'table' => self::get_operational_data_table_name(),
'where' => array( 'order_id' => $order->get_id() ),
'where_format' => '%d',
),
$row
);
}
// wc_order_addresses.
foreach ( array( 'billing', 'shipping' ) as $address_type ) {
$row = $this->get_db_row_from_order_changes( $changes, $this->{$address_type . '_address_column_mapping'} );
if ( $row ) {
$db_updates[] = array_merge(
array(
'table' => self::get_addresses_table_name(),
'where' => array(
'order_id' => $order->get_id(),
'address_type' => $address_type,
),
'where_format' => array( '%d', '%s' ),
),
$row
);
}
}
// Persist changes.
foreach ( $db_updates as $update ) {
$wpdb->update(
$update['table'],
$update['row'],
$update['where'],
array_values( $update['format'] ),
$update['where_format']
);
}
}
/**
* Produces an array with keys 'row' and 'format' that can be passed to `$wpdb->update()` as the `$data` and
* `$format` parameters. Values are taken from the order changes array and properly formatted for inclusion in the
* database.
*
* @param array $changes Order changes array.
* @param array $column_mapping Table column mapping.
* @return array
*/
private function get_db_row_from_order_changes( $changes, $column_mapping ) {
$row = array();
$row_format = array();
foreach ( $column_mapping as $column => $details ) {
if ( ! isset( $details['name'] ) || ! array_key_exists( $details['name'], $changes ) ) {
continue;
}
$row[ $column ] = $this->database_util->format_object_value_for_db( $changes[ $details['name'] ], $details['type'] );
$row_format[ $column ] = $this->database_util->get_wpdb_format_for_type( $details['type'] );
}
if ( ! $row ) {
return false;
}
return array(
'row' => $row,
'format' => $row_format,
);
}
//phpcs:disable Squiz.Commenting, Generic.Commenting
/**
* @param \WC_Order $order
*/
public function create( &$order ) {
throw new \Exception( 'Unimplemented' );
}
/**
* Method to update an order in the database.
*
* @param \WC_Order $order
*/
public function update( &$order ) {
global $wpdb;
// Before updating, ensure date paid is set if missing.
if (
! $order->get_date_paid( 'edit' )
&& version_compare( $order->get_version( 'edit' ), '3.0', '<' )
&& $order->has_status( apply_filters( 'woocommerce_payment_complete_order_status', $order->needs_processing() ? 'processing' : 'completed', $order->get_id(), $order ) ) // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
) {
$order->set_date_paid( $order->get_date_created( 'edit' ) );
}
if ( null === $order->get_date_created( 'edit' ) ) {
$order->set_date_created( time() );
}
$order->set_version( Constants::get_constant( 'WC_VERSION' ) );
// Fetch changes.
$changes = $order->get_changes();
// If address changed, store concatenated version to make searches faster.
foreach ( array( 'billing', 'shipping' ) as $address_type ) {
if ( isset( $changes[ $address_type ] ) ) {
$order->update_meta_data( "_{$address_type}_address_index", implode( ' ', $order->get_address( $address_type ) ) );
}
}
if ( ! isset( $changes['date_modified'] ) ) {
$order->set_date_modified( gmdate( 'Y-m-d H:i:s' ) );
}
// Update with latest changes.
$changes = $order->get_changes();
$this->persist_order_to_db( $order, true );
// Update download permissions if necessary.
if ( array_key_exists( 'billing_email', $changes ) || array_key_exists( 'customer_id', $changes ) ) {
$data_store = \WC_Data_Store::load( 'customer-download' );
$data_store->update_user_by_order_id( $order->get_id(), $order->get_customer_id(), $order->get_billing_email() );
}
// Mark user account as active.
if ( array_key_exists( 'customer_id', $changes ) ) {
wc_update_user_last_active( $order->get_customer_id() );
}
$order->save_meta_data();
$order->apply_changes();
$this->clear_caches( $order );
do_action( 'woocommerce_update_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
}
public function get_coupon_held_keys( $order, $coupon_id = null ) {
return array();
}
public function get_coupon_held_keys_for_users( $order, $coupon_id = null ) {
return array();
}
public function set_coupon_held_keys( $order, $held_keys, $held_keys_for_user ) {
throw new \Exception( 'Unimplemented' );
}
public function release_held_coupons( $order, $save = true ) {
throw new \Exception( 'Unimplemented' );
}
public function query( $query_vars ) {
return array();
}
public function get_order_item_type( $order, $order_item_id ) {
return 'line_item';
}
//phpcs:enable Squiz.Commenting, Generic.Commenting
/**
* Get the SQL needed to create all the tables needed for the custom orders table feature.
*
* @return string
*/
public function get_database_schema() {
global $wpdb;
$collate = $wpdb->has_cap( 'collation' ) ? $wpdb->get_charset_collate() : '';
$orders_table_name = $this->get_orders_table_name();
$addresses_table_name = $this->get_addresses_table_name();
$operational_data_table_name = $this->get_operational_data_table_name();
$meta_table = $this->get_meta_table_name();
$sql = "
CREATE TABLE $orders_table_name (
id bigint(20) unsigned auto_increment,
status varchar(20) null,
currency varchar(10) null,
tax_amount decimal(26,8) null,
total_amount decimal(26,8) null,
customer_id bigint(20) unsigned null,
billing_email varchar(320) null,
date_created_gmt datetime null,
date_updated_gmt datetime null,
parent_order_id bigint(20) unsigned null,
payment_method varchar(100) null,
payment_method_title text null,
transaction_id varchar(100) null,
ip_address varchar(100) null,
user_agent text null,
PRIMARY KEY (id),
KEY status (status),
KEY date_created (date_created_gmt),
KEY customer_id_billing_email (customer_id, billing_email)
) $collate;
CREATE TABLE $addresses_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NOT NULL,
address_type varchar(20) null,
first_name text null,
last_name text null,
company text null,
address_1 text null,
address_2 text null,
city text null,
state text null,
postcode text null,
country text null,
email varchar(320) null,
phone varchar(100) null,
KEY order_id (order_id),
KEY address_type_order_id (address_type, order_id)
) $collate;
CREATE TABLE $operational_data_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NULL,
created_via varchar(100) NULL,
woocommerce_version varchar(20) NULL,
prices_include_tax tinyint(1) NULL,
coupon_usages_are_counted tinyint(1) NULL,
download_permission_granted tinyint(1) NULL,
cart_hash varchar(100) NULL,
new_order_email_sent tinyint(1) NULL,
order_key varchar(100) NULL,
order_stock_reduced tinyint(1) NULL,
date_paid_gmt datetime NULL,
date_completed_gmt datetime NULL,
shipping_tax_amount decimal(26, 8) NULL,
shipping_total_amount decimal(26, 8) NULL,
discount_tax_amount decimal(26, 8) NULL,
discount_total_amount decimal(26, 8) NULL,
recorded_sales tinyint(1) NULL,
KEY order_id (order_id),
KEY order_key (order_key)
) $collate;
CREATE TABLE $meta_table (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned null,
meta_key varchar(255),
meta_value text null,
KEY meta_key_value (meta_key, meta_value(100))
) $collate;
";
return $sql;
}
/**
* Returns an array of meta for an object.
*
* @param WC_Data $object WC_Data object.
* @return array
*/
public function read_meta( &$object ) {
return $this->data_store_meta->read_meta( $object );
}
/**
* Deletes meta based on meta ID.
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing at least ->id).
*/
public function delete_meta( &$object, $meta ) {
return $this->data_store_meta->delete_meta( $object, $meta );
}
/**
* Add new piece of meta.
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->key and ->value).
* @return int meta ID
*/
public function add_meta( &$object, $meta ) {
return $this->data_store_meta->add_meta( $object, $meta );
}
/**
* Update meta.
*
* @param WC_Data $object WC_Data object.
* @param stdClass $meta (containing ->id, ->key and ->value).
*/
public function update_meta( &$object, $meta ) {
return $this->data_store_meta->update_meta( $object, $meta );
}
/**
* Returns list of metadata that is considered "internal".
*
* @return array
*/
public function get_internal_meta_keys() {
// XXX: This is mostly just to trick `WC_Data_Store_WP` for the time being.
return array(
'_customer_user',
'_order_key',
'_order_currency',
'_billing_first_name',
'_billing_last_name',
'_billing_company',
'_billing_address_1',
'_billing_address_2',
'_billing_city',
'_billing_state',
'_billing_postcode',
'_billing_country',
'_billing_email',
'_billing_phone',
'_shipping_first_name',
'_shipping_last_name',
'_shipping_company',
'_shipping_address_1',
'_shipping_address_2',
'_shipping_city',
'_shipping_state',
'_shipping_postcode',
'_shipping_country',
'_shipping_phone',
'_completed_date',
'_paid_date',
'_edit_lock',
'_edit_last',
'_cart_discount',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_payment_method',
'_payment_method_title',
'_transaction_id',
'_customer_ip_address',
'_customer_user_agent',
'_created_via',
'_order_version',
'_prices_include_tax',
'_date_completed',
'_date_paid',
'_payment_tokens',
'_billing_address_index',
'_shipping_address_index',
'_recorded_sales',
'_recorded_coupon_usage_counts',
'_download_permissions_granted',
'_order_stock_reduced',
);
}
}