s ) { if ( $dest_order->get_id() === $order->get_id() ) { $rows = $this->limit_hpos_update_to_props( $rows, $fields['props'] ); } return $rows; }; add_filter( 'woocommerce_orders_table_datastore_db_rows_for_order', $limit_cb, 10, 2 ); } } $datastore->update_order_from_object( $dest_order ); if ( 'hpos' === $destination_data_store && isset( $limit_cb ) ) { remove_filter( 'woocommerce_orders_table_datastore_db_rows_for_order', $limit_cb ); } } /** * Returns all metadata in an order object as an array. * * @param \WC_Order $order Order instance. * @return array Array of metadata grouped by meta key. */ private function order_meta_to_array( \WC_Order &$order ): array { $result = array(); foreach ( ArrayUtil::select( $order->get_meta_data(), 'get_data', ArrayUtil::SELECT_BY_OBJECT_METHOD ) as &$meta ) { if ( array_key_exists( $meta['key'], $result ) ) { $result[ $meta['key'] ] = array( $result[ $meta['key'] ] ); $result[ $meta['key'] ][] = $meta['value']; } else { $result[ $meta['key'] ] = $meta['value']; } } return $result; } /** * Returns names of all order base properties supported by HPOS. * * @return string[] Property names. */ private function get_order_base_props(): array { $base_props = array(); foreach ( $this->data_store->get_all_order_column_mappings() as $mapping ) { $base_props = array_merge( $base_props, array_column( $mapping, 'name' ) ); } return $base_props; } /** * Filters a set of HPOS row updates to those matching a specific set of order properties. * Called via the `woocommerce_orders_table_datastore_db_rows_for_order` filter in `backfill_order_to_datastore`. * * @param array $rows Details for the db update. * @param string[] $props Order property names. * @return array * @see OrdersTableDataStore::get_db_rows_for_order() */ private function limit_hpos_update_to_props( array $rows, array $props ) { // Determine HPOS columns corresponding to the props in the $props array. $allowed_columns = array(); foreach ( $this->data_store->get_all_order_column_mappings() as &$mapping ) { foreach ( $mapping as $column_name => &$column_data ) { if ( ! isset( $column_data['name'] ) || ! in_array( $column_data['name'], $props, true ) ) { continue; } $allowed_columns[ $column_data['name'] ] = $column_name; } } foreach ( $rows as $i => &$db_update ) { // Prevent accidental update of another prop by limiting columns to explicitly requested props. if ( ! array_intersect_key( $db_update['data'], array_flip( $allowed_columns ) ) ) { unset( $rows[ $i ] ); continue; } $allowed_column_names_with_ids = array_merge( $allowed_columns, array( 'id', 'order_id', 'address_type' ) ); $db_update['data'] = array_intersect_key( $db_update['data'], array_flip( $allowed_column_names_with_ids ) ); $db_update['format'] = array_intersect_key( $db_update['format'], array_flip( $allowed_column_names_with_ids ) ); } return $rows; } /** * Validates meta_keys and property names for a partial order backfill. * * @param array $fields An array possibly having entries with index 'meta_keys' and/or 'props', * corresponding to an array of order meta keys and/or order properties. * @param \WC_Abstract_Order $order The order being validated. * @throws \Exception When a validation error occurs. * @return void */ private function validate_backfill_fields( array $fields, \WC_Abstract_Order $order ) { if ( ! $fields ) { return; } if ( ! empty( $fields['meta_keys'] ) ) { $internal_meta_keys = array_unique( array_merge( $this->data_store->get_internal_meta_keys(), $this->data_store->get_cpt_data_store_instance()->get_internal_meta_keys() ) ); $possibly_internal_keys = array_intersect( $internal_meta_keys, $fields['meta_keys'] ); if ( ! empty( $possibly_internal_keys ) ) { throw new \Exception( esc_html( sprintf( // translators: %s is a comma separated list of metakey names. _n( '%s is an internal meta key. Use --props to set it.', '%s are internal meta keys. Use --props to set them.', count( $possibly_internal_keys ), 'woocommerce' ), implode( ', ', $possibly_internal_keys ) ) ) ); } } if ( ! empty( $fields['props'] ) ) { $invalid_props = array_filter( $fields['props'], function ( $prop_name ) use ( $order ) { return ! method_exists( $order, "get_{$prop_name}" ); } ); if ( ! empty( $invalid_props ) ) { throw new \Exception( esc_html( sprintf( // translators: %s is a list of order property names. _n( '%s is not a valid order property.', '%s are not valid order properties.', count( $invalid_props ), 'woocommerce' ), implode( ', ', $invalid_props ) ) ) ); } } } }