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, customer_note text null, PRIMARY KEY (id), KEY status (status), KEY date_created (date_created_gmt), KEY customer_id_billing_email (customer_id, billing_email({$composite_customer_id_email_length})), KEY billing_email (billing_email($max_index_length)), KEY type_status_date (type, status, date_created_gmt), KEY parent_order_id (parent_order_id), KEY date_updated (date_updated_gmt) ) $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), UNIQUE KEY address_type_order_id (address_type, order_id), KEY email (email($max_index_length)), KEY phone (phone) ) $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, UNIQUE 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(100), meta_value($composite_meta_value_index_length)), KEY order_id_meta_key_meta_value (order_id, meta_key(100), meta_value($composite_meta_value_index_length)) ) $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 ) { $raw_meta_data = $this->data_store_meta->read_meta( $object ); return $this->filter_raw_meta_data( $object, $raw_meta_data ); } /** * Deletes meta based on meta ID. * * @param WC_Data $object WC_Data object. * @param \stdClass $meta (containing at least ->id). * * @return bool */ public function delete_meta( &$object, $meta ) { if ( $this->should_backfill_post_record() && isset( $meta->id ) ) { // Let's get the actual meta key before its deleted for backfilling. We cannot delete just by ID because meta IDs are different in HPOS and posts tables. $db_meta = $this->data_store_meta->get_metadata_by_id( $meta->id ); if ( $db_meta ) { $meta->key = $db_meta->meta_key; $meta->value = $db_meta->meta_value; } } $delete_meta = $this->data_store_meta->delete_meta( $object, $meta ); $changes_applied = $this->after_meta_change( $object, $meta ); if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() && isset( $meta->key ) ) { self::$backfilling_order_ids[] = $object->get_id(); delete_post_meta( $object->get_id(), $meta->key, $meta->value ); self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) ); } return $delete_meta; } /** * Add new piece of meta. * * @param WC_Data $object WC_Data object. * @param \stdClass $meta (containing ->key and ->value). * * @return int|bool meta ID or false on failure */ public function add_meta( &$object, $meta ) { $add_meta = $this->data_store_meta->add_meta( $object, $meta ); $meta->id = $add_meta; $changes_applied = $this->after_meta_change( $object, $meta ); if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() ) { self::$backfilling_order_ids[] = $object->get_id(); add_post_meta( $object->get_id(), $meta->key, $meta->value ); self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) ); } return $add_meta; } /** * Update meta. * * @param WC_Data $object WC_Data object. * @param \stdClass $meta (containing ->id, ->key and ->value). * * @return bool The number of rows updated, or false on error. */ public function update_meta( &$object, $meta ) { $update_meta = $this->data_store_meta->update_meta( $object, $meta ); $changes_applied = $this->after_meta_change( $object, $meta ); if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() ) { self::$backfilling_order_ids[] = $object->get_id(); update_post_meta( $object->get_id(), $meta->key, $meta->value ); self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) ); } return $update_meta; } /** * Perform after meta change operations, including updating the date_modified field, clearing caches and applying changes. * * @param WC_Abstract_Order $order Order object. * @param \WC_Meta_Data $meta Metadata object. * * @return bool True if changes were applied, false otherwise. */ protected function after_meta_change( &$order, $meta ) { method_exists( $meta, 'apply_changes' ) && $meta->apply_changes(); // Prevent this happening multiple time in same request. if ( $this->should_save_after_meta_change( $order, $meta ) ) { $order->set_date_modified( current_time( 'mysql' ) ); $order->save(); return true; } else { $order_cache = wc_get_container()->get( OrderCache::class ); $order_cache->remove( $order->get_id() ); } return false; } /** * Helper function to check whether the modified date needs to be updated after a meta save. * * This method prevents order->save() call multiple times in the same request after any meta update by checking if: * 1. Order modified date is already the current date, no updates needed in this case. * 2. If there are changes already queued for order object, then we don't need to update the modified date as it will be updated ina subsequent save() call. * * @param WC_Order $order Order object. * @param \WC_Meta_Data|null $meta Metadata object. * * @return bool Whether the modified date needs to be updated. */ private function should_save_after_meta_change( $order, $meta = null ) { $current_time = $this->legacy_proxy->call_function( 'current_time', 'mysql', 1 ); $current_date_time = new \WC_DateTime( $current_time, new \DateTimeZone( 'GMT' ) ); return $order->get_date_modified() < $current_date_time && empty( $order->get_changes() ) && ( ! is_object( $meta ) || ! in_array( $meta->key, $this->ephemeral_meta_keys, true ) ); } }