diff --git a/.gitignore b/.gitignore
index bfbfcaa..22fa3f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
-/tests/coverage
-/vendor
-composer.lock
+tests/coverage
+vendor
diff --git a/.travis.yml b/.travis.yml
index 60337b8..211f928 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,7 @@
+language: php
sudo: false
dist: trusty
-language: php
-
notifications:
email: false
@@ -11,48 +10,34 @@ cache:
- $HOME/.composer/cache
matrix:
+ fast_finish: true
include:
- php: 7.2
- env: WP_VERSION=trunk
+ env: WP_VERSION=trunk WP_MULTISITE=0 RUN_PHPCS=1
+ - php: 7.2
+ env: WP_VERSION=trunk WP_MULTISITE=1
- php: 7.1
- env: WP_VERSION=latest
+ env: WP_VERSION=latest WP_MULTISITE=0
- php: 7.0
- env: WP_VERSION=latest
- - php: 5.6
- env: WP_VERSION=latest
- #- php: 5.6
- # env: WP_TRAVISCI=phpcs
- - php: 5.3
- env: WP_VERSION=latest
- dist: precise
+ env: WP_VERSION=latest WP_MULTISITE=0
+
+ # The following WooCommerce core test currently fails in multisite, with or without this
+ # plugin being active:
+ #
+ # WC_Tests_Setup_Functions::test_wizard_in_cart_payment_gateways()
+ allow_failures:
+ - php: 7.2
+ env: WP_VERSION=trunk WP_MULTISITE=1
+
before_script:
- export PATH="$HOME/.composer/vendor/bin:$PATH"
- - |
- if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
- phpenv config-rm xdebug.ini
- else
- echo "xdebug.ini does not exist"
- fi
- - |
- if [[ ! -z "$WP_VERSION" ]] ; then
- bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
- composer global require "phpunit/phpunit=4.8.*|5.7.*"
- fi
- - |
- if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then
- composer global require wp-coding-standards/wpcs
- phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs
- fi
+ - bash tests/bin/install-wp-tests.sh woocommerce_test root '' localhost $WP_VERSION
- composer install --prefer-source
script:
+ - phpunit
- |
- if [[ ! -z "$WP_VERSION" ]] ; then
- phpunit
- WP_MULTISITE=1 phpunit
- fi
- - |
- if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then
- phpcs
+ if [[ ${RUN_PHPCS} == 1 ]]; then
+ ./vendor/bin/phpcs
fi
diff --git a/composer.json b/composer.json
index 3b2a7fd..35017c6 100644
--- a/composer.json
+++ b/composer.json
@@ -8,11 +8,23 @@
"composer/installers": "^1.4",
"xrstf/composer-php52": "^1.0"
},
+ "require-dev": {
+ "php": "^7.0",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
+ "wimg/php-compatibility": "^8.1",
+ "woocommerce/woocommerce": "dev-master",
+ "woocommerce/woocommerce-sniffs": "^0.0.1",
+ "wp-coding-standards/wpcs": "^0.14"
+ },
"autoload": {
- "classmap": ["includes"]
+ "classmap": [
+ "includes"
+ ]
},
"autoload-dev": {
- "classmap": ["tests/test-tools"]
+ "classmap": [
+ "vendor/woocommerce/woocommerce/tests/framework"
+ ]
},
"scripts": {
"post-install-cmd": [
@@ -25,7 +37,14 @@
"xrstf\\Composer52\\Generator::onPostInstallCmd"
],
"test-coverage": [
- "phpunit --coverage-html=tests/coverage"
+ "phpunit --testsuite=plugin --coverage-html=tests/coverage"
]
+ },
+ "extra": {
+ "installer-paths": {
+ "vendor/{$vendor}/{$name}": [
+ "woocommerce/woocommerce"
+ ]
+ }
}
}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..5ac7941
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,470 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "fd86f99767fa5412fdd10e6971be6a34",
+ "packages": [
+ {
+ "name": "composer/installers",
+ "version": "v1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/installers.git",
+ "reference": "049797d727261bf27f2690430d935067710049c2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/installers/zipball/049797d727261bf27f2690430d935067710049c2",
+ "reference": "049797d727261bf27f2690430d935067710049c2",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0"
+ },
+ "replace": {
+ "roundcube/plugin-installer": "*",
+ "shama/baton": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Composer\\Installers\\Plugin",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Installers\\": "src/Composer/Installers"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "description": "A multi-framework Composer library installer",
+ "homepage": "https://composer.github.io/installers/",
+ "keywords": [
+ "Craft",
+ "Dolibarr",
+ "Eliasis",
+ "Hurad",
+ "ImageCMS",
+ "Kanboard",
+ "Lan Management System",
+ "MODX Evo",
+ "Mautic",
+ "Maya",
+ "OXID",
+ "Plentymarkets",
+ "Porto",
+ "RadPHP",
+ "SMF",
+ "Thelia",
+ "WolfCMS",
+ "agl",
+ "aimeos",
+ "annotatecms",
+ "attogram",
+ "bitrix",
+ "cakephp",
+ "chef",
+ "cockpit",
+ "codeigniter",
+ "concrete5",
+ "croogo",
+ "dokuwiki",
+ "drupal",
+ "eZ Platform",
+ "elgg",
+ "expressionengine",
+ "fuelphp",
+ "grav",
+ "installer",
+ "itop",
+ "joomla",
+ "kohana",
+ "laravel",
+ "lavalite",
+ "lithium",
+ "magento",
+ "majima",
+ "mako",
+ "mediawiki",
+ "modulework",
+ "modx",
+ "moodle",
+ "osclass",
+ "phpbb",
+ "piwik",
+ "ppi",
+ "puppet",
+ "pxcms",
+ "reindex",
+ "roundcube",
+ "shopware",
+ "silverstripe",
+ "sydes",
+ "symfony",
+ "typo3",
+ "wordpress",
+ "yawik",
+ "zend",
+ "zikula"
+ ],
+ "time": "2017-12-29T09:13:20+00:00"
+ },
+ {
+ "name": "xrstf/composer-php52",
+ "version": "v1.0.20",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer-php52/composer-php52.git",
+ "reference": "bd41459d5e27df8d33057842b32377c39e97a5a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer-php52/composer-php52/zipball/bd41459d5e27df8d33057842b32377c39e97a5a8",
+ "reference": "bd41459d5e27df8d33057842b32377c39e97a5a8",
+ "shasum": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-default": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "xrstf\\Composer52": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "time": "2016-04-16T21:52:24+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v0.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
+ "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/2e41850d5f7797cbb1af7b030d245b3b24e63a08",
+ "reference": "2e41850d5f7797cbb1af7b030d245b3b24e63a08",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0",
+ "php": "^5.3|^7",
+ "squizlabs/php_codesniffer": "*"
+ },
+ "require-dev": {
+ "composer/composer": "*",
+ "wimg/php-compatibility": "^8.0"
+ },
+ "suggest": {
+ "dealerdirect/qa-tools": "All the PHP QA tools you'll need"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "f.nijhof@dealerdirect.nl",
+ "homepage": "http://workingatdealerdirect.eu",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "homepage": "http://workingatdealerdirect.eu",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "time": "2017-12-06T16:27:17+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "d7c00c3000ac0ce79c96fcbfef86b49a71158cd1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7c00c3000ac0ce79c96fcbfef86b49a71158cd1",
+ "reference": "d7c00c3000ac0ce79c96fcbfef86b49a71158cd1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "http://www.squizlabs.com/php-codesniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2017-12-19T21:44:46+00:00"
+ },
+ {
+ "name": "wimg/php-compatibility",
+ "version": "8.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/wimg/PHPCompatibility.git",
+ "reference": "4ac01e4fe8faaa4f8d3b3cd06ea92e5418ce472e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/wimg/PHPCompatibility/zipball/4ac01e4fe8faaa4f8d3b3cd06ea92e5418ce472e",
+ "reference": "4ac01e4fe8faaa4f8d3b3cd06ea92e5418ce472e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "squizlabs/php_codesniffer": "^2.2 || ^3.0.2"
+ },
+ "conflict": {
+ "squizlabs/php_codesniffer": "2.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "autoload": {
+ "psr-4": {
+ "PHPCompatibility\\": "PHPCompatibility/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Wim Godden",
+ "role": "lead"
+ }
+ ],
+ "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP version compatibility.",
+ "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards"
+ ],
+ "time": "2017-12-27T21:58:38+00:00"
+ },
+ {
+ "name": "woocommerce/woocommerce",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/woocommerce/woocommerce.git",
+ "reference": "a887b49bb489852280501d1774dc05058bb47b7d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/woocommerce/woocommerce/zipball/a887b49bb489852280501d1774dc05058bb47b7d",
+ "reference": "a887b49bb489852280501d1774dc05058bb47b7d",
+ "shasum": ""
+ },
+ "require": {
+ "composer/installers": "~1.2"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3",
+ "phpunit/phpunit": "6.2.3",
+ "squizlabs/php_codesniffer": "*",
+ "wimg/php-compatibility": "^8.0",
+ "woocommerce/woocommerce-git-hooks": "*",
+ "woocommerce/woocommerce-sniffs": "*",
+ "wp-coding-standards/wpcs": "^0.14"
+ },
+ "type": "wordpress-plugin",
+ "extra": {
+ "scripts-description": {
+ "test": "Run unit tests",
+ "phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer",
+ "phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "description": "An eCommerce toolkit that helps you sell anything. Beautifully.",
+ "homepage": "https://woocommerce.com/",
+ "time": "2018-01-11T14:34:41+00:00"
+ },
+ {
+ "name": "woocommerce/woocommerce-sniffs",
+ "version": "0.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/woocommerce/woocommerce-sniffs.git",
+ "reference": "383d5b361c1d7532ae1ca6156fd7619fd37bbc05"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/383d5b361c1d7532ae1ca6156fd7619fd37bbc05",
+ "reference": "383d5b361c1d7532ae1ca6156fd7619fd37bbc05",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "squizlabs/php_codesniffer": "^3.0.2"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Claudio Sanches",
+ "email": "claudio@automattic.com"
+ }
+ ],
+ "description": "WooCommerce sniffs",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "woocommerce",
+ "wordpress"
+ ],
+ "time": "2017-12-21T22:52:52+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "0.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git",
+ "reference": "8cadf48fa1c70b2381988e0a79e029e011a8f41c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress-Coding-Standards/WordPress-Coding-Standards/zipball/8cadf48fa1c70b2381988e0a79e029e011a8f41c",
+ "reference": "8cadf48fa1c70b2381988e0a79e029e011a8f41c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "squizlabs/php_codesniffer": "^2.9.0 || ^3.0.2"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "wordpress"
+ ],
+ "time": "2017-11-01T15:10:46+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "woocommerce/woocommerce": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=5.2"
+ },
+ "platform-dev": {
+ "php": "^7.0"
+ }
+}
diff --git a/includes/class-wc-custom-order-table-install.php b/includes/class-wc-custom-order-table-install.php
index 09c3882..03d37c9 100644
--- a/includes/class-wc-custom-order-table-install.php
+++ b/includes/class-wc-custom-order-table-install.php
@@ -57,6 +57,7 @@ protected static function install_tables() {
order_id BIGINT UNSIGNED NOT NULL,
order_key varchar(100) NOT NULL,
customer_id BIGINT UNSIGNED NOT NULL,
+ billing_index varchar(255) NOT NULL,
billing_first_name varchar(100) NOT NULL,
billing_last_name varchar(100) NOT NULL,
billing_company varchar(100) NOT NULL,
@@ -68,6 +69,7 @@ protected static function install_tables() {
billing_country varchar(100) NOT NULL,
billing_email varchar(200) NOT NULL,
billing_phone varchar(200) NOT NULL,
+ shipping_index varchar(255) NOT NULL,
shipping_first_name varchar(100) NOT NULL,
shipping_last_name varchar(100) NOT NULL,
shipping_company varchar(100) NOT NULL,
@@ -79,21 +81,21 @@ protected static function install_tables() {
shipping_country varchar(100) NOT NULL,
payment_method varchar(100) NOT NULL,
payment_method_title varchar(100) NOT NULL,
- discount_total float NOT NULL DEFAULT 0,
- discount_tax float NOT NULL DEFAULT 0,
- shipping_total float NOT NULL DEFAULT 0,
- shipping_tax float NOT NULL DEFAULT 0,
- cart_tax float NOT NULL DEFAULT 0,
- total float NOT NULL DEFAULT 0,
+ discount_total varchar(100) NOT NULL DEFAULT 0,
+ discount_tax varchar(100) NOT NULL DEFAULT 0,
+ shipping_total varchar(100) NOT NULL DEFAULT 0,
+ shipping_tax varchar(100) NOT NULL DEFAULT 0,
+ cart_tax varchar(100) NOT NULL DEFAULT 0,
+ total varchar(100) NOT NULL DEFAULT 0,
version varchar(16) NOT NULL,
currency varchar(3) NOT NULL,
- prices_include_tax tinyint(1) NOT NULL,
+ prices_include_tax varchar(3) NOT NULL,
transaction_id varchar(200) NOT NULL,
customer_ip_address varchar(40) NOT NULL,
customer_user_agent varchar(200) NOT NULL,
created_via varchar(200) NOT NULL,
- date_completed datetime DEFAULT NULL,
- date_paid datetime DEFAULT NULL,
+ date_completed varchar(20) DEFAULT NULL,
+ date_paid varchar(20) DEFAULT NULL,
cart_hash varchar(32) NOT NULL,
PRIMARY KEY (order_id),
UNIQUE KEY `order_key` (`order_key`),
diff --git a/includes/class-wc-custom-order-table.php b/includes/class-wc-custom-order-table.php
index 08fb2f2..264f505 100644
--- a/includes/class-wc-custom-order-table.php
+++ b/includes/class-wc-custom-order-table.php
@@ -28,11 +28,11 @@ public function setup() {
$this->table_name = $wpdb->prefix . 'woocommerce_orders';
- // Inject the plugin into order processing.
+ // Use the plugin's custom data stores for customers and orders.
+ add_filter( 'woocommerce_customer_data_store', array( $this, 'customer_data_store' ) );
add_filter( 'woocommerce_order_data_store', array( $this, 'order_data_store' ) );
- add_filter( 'posts_join', array( $this, 'wp_query_customer_query' ), 10, 2 );
- // Register the CLI command if we're running WP_CLI.
+ // If we're in a WP-CLI context, load the WP-CLI command.
if ( defined( 'WP_CLI' ) && WP_CLI ) {
WP_CLI::add_command( 'wc-order-table', 'WC_Custom_Order_Table_CLI' );
}
@@ -53,89 +53,20 @@ public function get_table_name() {
}
/**
- * Retrieve the class name of the WooCommerce order data store.
+ * Retrieve the class name of the WooCommerce customer data store.
*
* @return string The data store class name.
*/
- public function order_data_store() {
- return 'WC_Order_Data_Store_Custom_Table';
+ public function customer_data_store() {
+ return 'WC_Customer_Data_Store_Custom_Table';
}
/**
- * Modify posts_join queries when the query includes wc_customer_query.
- *
- * @global $wpdb
- *
- * @param string $join The SQL JOIN statement.
- * @param WP_Query $wp_query The current WP_Query object.
- *
- * @return string The [potentially] filtered JOIN statement.
- */
- public function wp_query_customer_query( $join, $wp_query ) {
- global $wpdb;
-
- // If there is no wc_customer_query then no need to process anything.
- if ( ! isset( $wp_query->query_vars['wc_customer_query'] ) ) {
- return $join;
- }
-
- $customer_query = $this->generate_wc_customer_query( $wp_query->query_vars['wc_customer_query'] );
- $query_parts = array();
-
- if ( ! empty( $customer_query['emails'] ) ) {
- $emails = '\'' . implode( '\', \'', array_unique( $customer_query['emails'] ) ) . '\'';
- $query_parts[] = "{$this->get_table_name()}.billing_email IN ( {$emails} )";
- }
-
- if ( ! empty( $customer_query['users'] ) ) {
- $users = implode( ',', array_unique( $customer_query['users'] ) );
- $query_parts[] = "{$this->get_table_name()}.customer_id IN ( {$users} )";
- }
-
- if ( ! empty( $query_parts ) ) {
- $query = '( ' . implode( ') OR (', $query_parts ) . ' )';
- $join .= "JOIN {$this->get_table_name()} ON
- ( {$wpdb->posts}.ID = {$this->get_table_name()}.order_id )
- AND ( {$query} )";
- }
-
- return $join;
- }
-
- /**
- * Given a wc_customer_query argument, construct an array of customers grouped by either email
- * address or user ID.
- *
- * @param array $values Query arguments from WP_Query->query_vars['wc_customer_query'].
+ * Retrieve the class name of the WooCommerce order data store.
*
- * @return array A complex array with two keys: "emails" and "users".
+ * @return string The data store class name.
*/
- public function generate_wc_customer_query( $values ) {
- $customer_query = array(
- 'emails' => array(),
- 'users' => array(),
- );
-
- foreach ( $values as $value ) {
- // If the value is an array, call this method recursively and merge the results.
- if ( is_array( $value ) ) {
- $query = $this->generate_wc_customer_query( $value );
-
- if ( is_array( $query['emails'] ) ) {
- $customer_query['emails'] = array_merge( $customer_query['emails'], $query['emails'] );
- }
-
- if ( is_array( $query['users'] ) ) {
- $customer_query['users'] = array_merge( $customer_query['users'], $query['users'] );
- }
- } elseif ( is_email( $value ) ) {
- $customer_query['emails'][] = sanitize_email( $value );
-
- } else {
- $customer_query['users'][] = strval( absint( $value ) );
- }
- }
-
- return $customer_query;
+ public function order_data_store() {
+ return 'WC_Order_Data_Store_Custom_Table';
}
}
diff --git a/includes/class-wc-customer-data-store-custom-table.php b/includes/class-wc-customer-data-store-custom-table.php
new file mode 100644
index 0000000..e322934
--- /dev/null
+++ b/includes/class-wc-customer-data-store-custom-table.php
@@ -0,0 +1,231 @@
+get_table_name();
+ $statuses = wc_get_order_statuses();
+ $last_order = $wpdb->get_var( $wpdb->prepare( "
+ SELECT posts.ID FROM $wpdb->posts AS posts
+ LEFT JOIN " . esc_sql( $table ) . " AS meta on posts.ID = meta.order_id
+ WHERE meta.customer_id = %d
+ AND posts.post_type = 'shop_order'
+ AND posts.post_status IN (" . implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ) . ')
+ ORDER BY posts.ID DESC LIMIT 1',
+ array_merge( array( $customer->get_id() ), array_keys( $statuses ) )
+ ) ); // WPCS: DB call OK.
+
+ return $last_order ? wc_get_order( (int) $last_order ) : false;
+ }
+
+ /**
+ * Return the number of orders this customer has.
+ *
+ * @global $wpdb
+ *
+ * @param WC_Customer $customer The WC_Customer object, passed by reference.
+ *
+ * @return int The number of orders for this customer.
+ */
+ public function get_order_count( &$customer ) {
+ global $wpdb;
+
+ $count = get_user_meta( $customer->get_id(), '_order_count', true );
+
+ if ( '' === $count ) {
+ $table = wc_custom_order_table()->get_table_name();
+ $statuses = wc_get_order_statuses();
+ $count = $wpdb->get_var( $wpdb->prepare( "
+ SELECT COUNT(*) FROM $wpdb->posts as posts
+ LEFT JOIN " . esc_sql( $table ) . " AS meta ON posts.ID = meta.order_id
+ WHERE meta.customer_id = %d
+ AND posts.post_type = 'shop_order'
+ AND posts.post_status IN (" . implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ) . ')',
+ array_merge( array( $customer->get_id() ), array_keys( $statuses ) )
+ ) ); // WPCS: DB call OK.
+ update_user_meta( $customer->get_id(), '_order_count', $count );
+ }
+
+ return (int) $count;
+ }
+
+ /**
+ * Return how much money this customer has spent.
+ *
+ * @global $wpdb
+ *
+ * @param WC_Customer $customer The WC_Customer object, passed by reference.
+ *
+ * @return float The total amount spent by the customer.
+ */
+ public function get_total_spent( &$customer ) {
+ global $wpdb;
+
+ $spent = get_user_meta( $customer->get_id(), '_money_spent', true );
+
+ /**
+ * Filter the total amount spent by the given customer across all orders.
+ *
+ * @param float $spent The total of all orders for this customer.
+ * @param WC_Customer $customer The customer being queried.
+ */
+ $spent = apply_filters( 'woocommerce_customer_get_total_spent', $spent, $customer );
+
+ // If there's no saved value, attempt to calculate one.
+ if ( '' === $spent ) {
+ $table = wc_custom_order_table()->get_table_name();
+ $statuses = array_map( 'self::prefix_wc_status', wc_get_is_paid_statuses() );
+ $sql = $wpdb->prepare( "
+ SELECT SUM(meta.total) FROM $wpdb->posts as posts
+ LEFT JOIN " . esc_sql( $table ) . " AS meta ON posts.ID = meta.order_id
+ WHERE meta.customer_id = %d
+ AND posts.post_type = 'shop_order'
+ AND posts.post_status IN (" . implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ) . ')',
+ array_merge( array( $customer->get_id() ), $statuses )
+ );
+
+ /**
+ * Filter the MySQL query used to determine how much a customer has spent across all orders.
+ *
+ * @param string $sql The prepared MySQL statement.
+ * @param WC_Customer $customer The customer being queried.
+ */
+ $sql = apply_filters( 'woocommerce_customer_get_total_spent_query', $sql, $customer );
+ $spent = (float) $wpdb->get_var( $sql ); // WPCS: Unprepared SQL OK, DB call OK.
+
+ update_user_meta( $customer->get_id(), '_money_spent', $spent );
+ }
+
+ return wc_format_decimal( $spent, 2 );
+ }
+
+ /**
+ * Short-circuit the wc_customer_bought_product() function.
+ *
+ * @see wc_customer_bought_product()
+ *
+ * @global $wpdb
+ *
+ * @param bool $purchased Whether or not the customer has purchased the product.
+ * @param string $customer_email Customer email to check.
+ * @param int $user_id User ID to check.
+ * @param int $product_id Product ID to check.
+ *
+ * @return bool Whether or not the customer has already purchased the given product ID.
+ */
+ public static function pre_customer_bought_product( $purchased, $customer_email, $user_id, $product_id ) {
+ global $wpdb;
+
+ $transient_name = 'wc_cbp_' . md5( $customer_email . $user_id . WC_Cache_Helper::get_transient_version( 'orders' ) );
+ $result = get_transient( $transient_name );
+
+ if ( false === $result ) {
+ $customer_data = array( $user_id );
+
+ if ( $user_id ) {
+ $user = get_user_by( 'id', $user_id );
+
+ if ( isset( $user->user_email ) ) {
+ $customer_data[] = $user->user_email;
+ }
+ }
+
+ if ( is_email( $customer_email ) ) {
+ $customer_data[] = $customer_email;
+ }
+
+ $customer_data = array_map( 'esc_sql', array_filter( array_unique( $customer_data ) ) );
+ $statuses = array_map( 'self::prefix_wc_status', wc_get_is_paid_statuses() );
+
+ if ( 0 === count( $customer_data ) ) {
+ return false;
+ }
+
+ $table = wc_custom_order_table()->get_table_name();
+ $result = $wpdb->get_col( $wpdb->prepare( "
+ SELECT im.meta_value FROM {$wpdb->posts} AS p
+ INNER JOIN " . esc_sql( $table ) . " AS pm ON p.ID = pm.order_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
+ INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
+ WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $statuses ), '%s' ) ) . ')
+ AND (
+ pm.billing_email IN (' . implode( ', ', array_fill( 0, count( $customer_data ), '%s' ) ) . ')
+ OR pm.customer_id IN (' . implode( ', ', array_fill( 0, count( $customer_data ), '%s' ) ) . ")
+ )
+ AND im.meta_key IN ( '_product_id', '_variation_id' )
+ AND im.meta_value != 0",
+ array_merge( $statuses, $customer_data, $customer_data )
+ ) ); // WPCS: DB call OK.
+ $result = array_map( 'absint', $result );
+
+ set_transient( $transient_name, $result, DAY_IN_SECONDS * 30 );
+ }
+
+ return in_array( (int) $product_id, $result, true );
+ }
+
+ /**
+ * Reset customer_id on orders when a user is deleted.
+ *
+ * @param int $user_id The ID of the deleted user.
+ */
+ public static function reset_order_customer_id_on_deleted_user( $user_id ) {
+ global $wpdb;
+
+ $wpdb->update(
+ wc_custom_order_table()->get_table_name(),
+ array( 'customer_id' => 0 ),
+ array( 'customer_id' => $user_id )
+ ); // WPCS: DB call OK.
+ }
+
+ /**
+ * Helper function to prefix a status with 'wc-'.
+ *
+ * Statuses that already contain the prefix will be skipped.
+ *
+ * @param string $status The status to prefix.
+ *
+ * @return string The status with 'wc-' prefixed.
+ */
+ public static function prefix_wc_status( $status ) {
+ if ( 'wc-' === substr( $status, 0, 3 ) ) {
+ return $status;
+ }
+
+ return 'wc-' . $status;
+ }
+}
diff --git a/includes/class-wc-order-data-store-custom-table.php b/includes/class-wc-order-data-store-custom-table.php
index e047b41..aa4c5dd 100644
--- a/includes/class-wc-order-data-store-custom-table.php
+++ b/includes/class-wc-order-data-store-custom-table.php
@@ -7,12 +7,12 @@
*/
/**
- * Extension of the Abstract_WC_Order_Data_Store_CPT class, designed to map data between
- * WooCommerce and the custom database table.
+ * Extend the WC_Order_Data_Store_CPT class, overloading methods that require database access in
+ * order to use the new table.
*
- * Orders are still treated as posts within WordPress, but the data is stored in a separate table.
+ * Orders are still treated as posts within WordPress, but the meta is stored in a separate table.
*/
-class WC_Order_Data_Store_Custom_Table extends Abstract_WC_Order_Data_Store_CPT implements WC_Object_Data_Store_Interface, WC_Order_Data_Store_Interface {
+class WC_Order_Data_Store_Custom_Table extends WC_Order_Data_Store_CPT {
/**
* Set to true when creating so we know to insert meta data.
@@ -22,64 +22,68 @@ class WC_Order_Data_Store_Custom_Table extends Abstract_WC_Order_Data_Store_CPT
protected $creating = false;
/**
- * Map table columns to related postmeta keys.
- *
- * @var array
+ * Hook into WooCommerce database queries related to orders.
*/
- protected $postmeta_mapping = array(
- 'order_key' => '_order_key',
- 'customer_id' => '_customer_user',
- 'payment_method' => '_payment_method',
- 'payment_method_title' => '_payment_method_title',
- 'transaction_id' => '_transaction_id',
- 'customer_ip_address' => '_customer_ip_address',
- 'customer_user_agent' => '_customer_user_agent',
- 'created_via' => '_created_via',
- 'date_completed' => '_date_completed',
- 'date_paid' => '_date_paid',
- 'cart_hash' => '_cart_hash',
-
- 'billing_first_name' => '_billing_first_name',
- 'billing_last_name' => '_billing_last_name',
- 'billing_company' => '_billing_company',
- 'billing_address_1' => '_billing_address_1',
- 'billing_address_2' => '_billing_address_2',
- 'billing_city' => '_billing_city',
- 'billing_state' => '_billing_state',
- 'billing_postcode' => '_billing_postcode',
- 'billing_country' => '_billing_country',
- 'billing_email' => '_billing_email',
- 'billing_phone' => '_billing_phone',
-
- 'shipping_first_name' => '_shipping_first_name',
- 'shipping_last_name' => '_shipping_last_name',
- 'shipping_company' => '_shipping_company',
- 'shipping_address_1' => '_shipping_address_1',
- 'shipping_address_2' => '_shipping_address_2',
- 'shipping_city' => '_shipping_city',
- 'shipping_state' => '_shipping_state',
- 'shipping_postcode' => '_shipping_postcode',
- 'shipping_country' => '_shipping_country',
-
- 'discount_total' => '_cart_discount',
- 'discount_tax' => '_cart_discount_tax',
- 'shipping_total' => '_order_shipping',
- 'shipping_tax' => '_order_shipping_tax',
- 'cart_tax' => '_order_tax',
- 'total' => '_order_total',
-
- 'version' => '_order_version',
- 'currency' => '_order_currency',
- 'prices_include_tax' => '_prices_include_tax',
- );
+ public function __construct() {
+
+ // When creating a WooCommerce order data store request, filter the MySQL query.
+ add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', __CLASS__ . '::filter_database_queries', 10, 2 );
+ }
/**
* Retrieve the database table column => post_meta mapping.
*
* @return array An array of database columns and their corresponding post_meta keys.
*/
- public function get_postmeta_mapping() {
- return $this->postmeta_mapping;
+ public static function get_postmeta_mapping() {
+ return array(
+ 'order_key' => '_order_key',
+ 'customer_id' => '_customer_user',
+ 'payment_method' => '_payment_method',
+ 'payment_method_title' => '_payment_method_title',
+ 'transaction_id' => '_transaction_id',
+ 'customer_ip_address' => '_customer_ip_address',
+ 'customer_user_agent' => '_customer_user_agent',
+ 'created_via' => '_created_via',
+ 'date_completed' => '_date_completed',
+ 'date_paid' => '_date_paid',
+ 'cart_hash' => '_cart_hash',
+
+ 'billing_index' => '_billing_address_index',
+ 'billing_first_name' => '_billing_first_name',
+ 'billing_last_name' => '_billing_last_name',
+ 'billing_company' => '_billing_company',
+ 'billing_address_1' => '_billing_address_1',
+ 'billing_address_2' => '_billing_address_2',
+ 'billing_city' => '_billing_city',
+ 'billing_state' => '_billing_state',
+ 'billing_postcode' => '_billing_postcode',
+ 'billing_country' => '_billing_country',
+ 'billing_email' => '_billing_email',
+ 'billing_phone' => '_billing_phone',
+
+ 'shipping_index' => '_shipping_address_index',
+ 'shipping_first_name' => '_shipping_first_name',
+ 'shipping_last_name' => '_shipping_last_name',
+ 'shipping_company' => '_shipping_company',
+ 'shipping_address_1' => '_shipping_address_1',
+ 'shipping_address_2' => '_shipping_address_2',
+ 'shipping_city' => '_shipping_city',
+ 'shipping_state' => '_shipping_state',
+ 'shipping_postcode' => '_shipping_postcode',
+ 'shipping_country' => '_shipping_country',
+
+ 'discount_total' => '_cart_discount',
+ 'discount_tax' => '_cart_discount_tax',
+ 'shipping_total' => '_order_shipping',
+ 'shipping_tax' => '_order_shipping_tax',
+ 'cart_tax' => '_order_tax',
+ 'total' => '_order_total',
+
+ 'version' => '_order_version',
+ 'currency' => '_order_currency',
+ 'prices_include_tax' => '_prices_include_tax',
+ );
}
/**
@@ -88,14 +92,6 @@ public function get_postmeta_mapping() {
* @param WC_Order $order The order object, passed by reference.
*/
public function create( &$order ) {
- /**
- * Filter the generated order ID.
- *
- * @param string $order_key The uniquely-generated ID for this order.
- */
- $order_key = apply_filters( 'woocommerce_generate_order_key', uniqid( 'order_' ) );
-
- $order->set_order_key( 'wc_' . $order_key );
$this->creating = true;
parent::create( $order );
@@ -116,13 +112,12 @@ public function delete( &$order, $args = array() ) {
parent::delete( $order, $args );
- if ( $args['force_delete'] || 0 == $order->get_id() ) {
+ // Delete the database row if force_delete is true.
+ if ( isset( $args['force_delete'] ) && $args['force_delete'] ) {
$wpdb->delete(
"{$wpdb->prefix}woocommerce_orders",
- array(
- 'order_id' => $order_id,
- )
- );
+ array( 'order_id' => $order_id )
+ ); // WPCS: DB call OK.
}
}
@@ -135,8 +130,6 @@ public function delete( &$order, $args = array() ) {
protected function read_order_data( &$order, $post_object ) {
global $wpdb;
- parent::read_order_data( $order, $post_object );
-
$data = $this->get_order_data_from_table( $order );
if ( ! empty( $data ) ) {
@@ -162,7 +155,16 @@ protected function read_order_data( &$order, $post_object ) {
public function get_order_data_from_table( $order ) {
global $wpdb;
- return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_orders WHERE order_id = %d;", $order->get_id() ) );
+ $table = wc_custom_order_table()->get_table_name();
+ $data = $wpdb->get_row( $wpdb->prepare(
+ 'SELECT * FROM ' . esc_sql( $table ) . ' WHERE order_id = %d LIMIT 1',
+ $order->get_id()
+ ), ARRAY_A ); // WPCS: DB call OK.
+
+ // Expand anything that might need assistance.
+ $data['prices_include_tax'] = wc_string_to_bool( $data['prices_include_tax'] );
+
+ return $data;
}
/**
@@ -176,7 +178,10 @@ public function get_order_data_from_table( $order ) {
protected function update_post_meta( &$order ) {
global $wpdb;
- $edit_data = array(
+ $table = wc_custom_order_table()->get_table_name();
+ $changes = array();
+ $order_data = array(
+ 'order_id' => $order->get_id( 'edit' ),
'order_key' => $order->get_order_key( 'edit' ),
'customer_id' => $order->get_customer_id( 'edit' ),
'payment_method' => $order->get_payment_method( 'edit' ),
@@ -189,6 +194,7 @@ protected function update_post_meta( &$order ) {
'date_paid' => $order->get_date_paid( 'edit' ),
'cart_hash' => $order->get_cart_hash( 'edit' ),
+ 'billing_index' => implode( ' ', $order->get_address( 'billing' ) ),
'billing_first_name' => $order->get_billing_first_name( 'edit' ),
'billing_last_name' => $order->get_billing_last_name( 'edit' ),
'billing_company' => $order->get_billing_company( 'edit' ),
@@ -202,6 +208,7 @@ protected function update_post_meta( &$order ) {
'billing_email' => $order->get_billing_email( 'edit' ),
'billing_phone' => $order->get_billing_phone( 'edit' ),
+ 'shipping_index' => implode( ' ', $order->get_address( 'shipping' ) ),
'shipping_first_name' => $order->get_shipping_first_name( 'edit' ),
'shipping_last_name' => $order->get_shipping_last_name( 'edit' ),
'shipping_company' => $order->get_shipping_company( 'edit' ),
@@ -216,44 +223,54 @@ protected function update_post_meta( &$order ) {
'discount_tax' => $order->get_discount_tax( 'edit' ),
'shipping_total' => $order->get_shipping_total( 'edit' ),
'shipping_tax' => $order->get_shipping_tax( 'edit' ),
- 'cart_tax' => $order->get_total_tax( 'edit' ),
+ 'cart_tax' => $order->get_cart_tax( 'edit' ),
'total' => $order->get_total( 'edit' ),
'version' => $order->get_version( 'edit' ),
'currency' => $order->get_currency( 'edit' ),
- 'prices_include_tax' => $order->get_prices_include_tax( 'edit' ),
+ 'prices_include_tax' => wc_bool_to_string( $order->get_prices_include_tax( 'edit' ) ),
);
- $changes = array();
+ // Convert dates to timestamps, if they exist.
+ foreach ( array( 'date_completed', 'date_paid' ) as $date ) {
+ if ( $order_data[ $date ] instanceof WC_DateTime ) {
+ $order_data[ $date ] = $order_data[ $date ]->getTimestamp();
+ }
+ }
+ // Insert or update the database record.
if ( $this->creating ) {
- $wpdb->insert(
- "{$wpdb->prefix}woocommerce_orders",
- array_merge( array(
- 'order_id' => $order->get_id(),
- ), $edit_data )
- );
+ $wpdb->insert( $table, $order_data ); // WPCS: DB call OK.
- // We are no longer creating the order, it is created.
$this->creating = false;
+
} else {
- $changes = array_intersect_key( $edit_data, $order->get_changes() );
+ $changes = array_intersect_key( $order_data, $order->get_changes() );
+
+ /*
+ * WC_Order::get_changes() will mark all address fields as changed if one has changed.
+ *
+ * If any of these fields are present, be sure we update the index column.
+ */
+ if ( isset( $changes['billing_first_name'] ) ) {
+ $changes['billing_index'] = $order_data['billing_index'];
+ }
+
+ if ( isset( $changes['shipping_first_name'] ) ) {
+ $changes['shipping_index'] = $order_data['shipping_index'];
+ }
if ( ! empty( $changes ) ) {
- $wpdb->update(
- "{$wpdb->prefix}woocommerce_orders",
- $changes,
- array(
- 'order_id' => $order->get_id(),
- )
- );
+ $wpdb->update( $table, $changes, array( 'order_id' => $order->get_id() ) ); // WPCS: DB call OK.
}
}
$updated_props = array_keys( (array) $changes );
// If customer changed, update any downloadable permissions.
- if ( in_array( 'customer_user', $updated_props ) || in_array( 'billing_email', $updated_props ) ) {
+ $customer_props = array( 'customer_user', 'billing_email' );
+
+ if ( array_intersect( $customer_props, $updated_props ) ) {
$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() );
}
@@ -261,88 +278,6 @@ protected function update_post_meta( &$order ) {
do_action( 'woocommerce_order_object_updated_props', $order, $updated_props );
}
- /**
- * Excerpt for post.
- *
- * @param WC_Order $order The order object.
- *
- * @return string The post excerpt.
- */
- protected function get_post_excerpt( $order ) {
- return $order->get_customer_note();
- }
-
- /**
- * Get amount already refunded.
- *
- * @global $wpdb
- *
- * @param WC_Order $order The order object.
- *
- * @return string The refund amount.
- */
- public function get_total_refunded( $order ) {
- global $wpdb;
-
- $total = $wpdb->get_var( $wpdb->prepare( "
- SELECT SUM( postmeta.meta_value )
- FROM $wpdb->postmeta AS postmeta
- INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
- WHERE postmeta.meta_key = '_refund_amount'
- AND postmeta.post_id = posts.ID
- ", $order->get_id() ) );
-
- return $total;
- }
-
- /**
- * Get the total tax refunded.
- *
- * @global $wpdb
- *
- * @param WC_Order $order The order object.
- *
- * @return float The total refunded tax.
- */
- public function get_total_tax_refunded( $order ) {
- global $wpdb;
-
- $total = $wpdb->get_var( $wpdb->prepare( "
- SELECT SUM( order_itemmeta.meta_value )
- FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
- INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
- INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'tax' )
- WHERE order_itemmeta.order_item_id = order_items.order_item_id
- AND order_itemmeta.meta_key IN ('tax_amount', 'shipping_tax_amount')
- ", $order->get_id() ) );
-
- return abs( $total );
- }
-
- /**
- * Get the total shipping refunded.
- *
- * @global $wpdb
- *
- * @param WC_Order $order The order object.
- *
- * @return float The total refunded shipping.
- */
- public function get_total_shipping_refunded( $order ) {
- global $wpdb;
-
- $total = $wpdb->get_var( $wpdb->prepare( "
- SELECT SUM( order_itemmeta.meta_value )
- FROM {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta
- INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
- INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON ( order_items.order_id = posts.ID AND order_items.order_item_type = 'shipping' )
- WHERE order_itemmeta.order_item_id = order_items.order_item_id
- AND order_itemmeta.meta_key IN ('cost')
- ", $order->get_id() ) );
-
- return abs( $total );
- }
-
/**
* Finds an order ID based on its order key.
*
@@ -356,192 +291,7 @@ public function get_order_id_by_order_key( $order_key ) {
return $wpdb->get_var( $wpdb->prepare(
"SELECT order_id FROM {$wpdb->prefix}woocommerce_orders WHERE order_key = %s",
$order_key
- ) );
- }
-
- /**
- * Return count of orders with a specific status.
- *
- * @global $wpdb
- *
- * @param string $status The post_status to filter orders by.
- *
- * @return int The number of orders with that status.
- */
- public function get_order_count( $status ) {
- global $wpdb;
-
- return absint( $wpdb->get_var( $wpdb->prepare(
- "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'shop_order' AND post_status = %s",
- $status
- ) ) );
- }
-
- /**
- * Get all orders matching the passed in args.
- *
- * @see wc_get_orders()
- *
- * @param array $args {
- * Query arguments. All arguments are optional.
- *
- * @var string $type The post type. Default is 'shop_order'.
- * @var
- * }
- * @return object|array array of orders
- */
- public function get_orders( $args = array() ) {
- /**
- * Generate WP_Query args. This logic will change if orders are moved to
- * custom tables in the future.
- */
- $wp_query_args = array(
- 'post_type' => $args['type'] ? $args['type'] : 'shop_order',
- 'post_status' => $args['status'],
- 'posts_per_page' => $args['limit'],
- 'meta_query' => array(),
- 'fields' => 'ids',
- 'orderby' => $args['orderby'],
- 'order' => $args['order'],
- );
- $wc_customer_query = array();
-
- if ( ! empty( $args['customer'] ) ) {
- $values = is_array( $args['customer'] ) ? $args['customer'] : array( $args['customer'] );
- $wc_customer_query = array_merge( $wc_customer_query, $values );
- }
-
- if ( ! empty( $args['email'] ) ) {
- $values = is_array( $args['email'] ) ? $args['email'] : array( $args['email'] );
- $wc_customer_query = array_merge( $wc_customer_query, $values );
- }
-
- if ( ! empty( $wc_customer_query ) ) {
- $wp_query_args['wc_customer_query'] = $wc_customer_query;
- }
-
- /**
- * Standard Args
- */
- if ( ! is_null( $args['parent'] ) ) {
- $wp_query_args['post_parent'] = absint( $args['parent'] );
- }
-
- if ( ! is_null( $args['offset'] ) ) {
- $wp_query_args['offset'] = absint( $args['offset'] );
- } else {
- $wp_query_args['paged'] = absint( $args['page'] );
- }
-
- if ( ! empty( $args['exclude'] ) ) {
- $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
- }
-
- if ( ! $args['paginate'] ) {
- $wp_query_args['no_found_rows'] = true;
- }
-
- if ( ! empty( $args['date_before'] ) ) {
- $wp_query_args['date_query']['before'] = $args['date_before'];
- }
-
- if ( ! empty( $args['date_after'] ) ) {
- $wp_query_args['date_query']['after'] = $args['date_after'];
- }
-
- /**
- * Filter WP_Query arguments when retrieving orders from the database.
- *
- * @param array $wp_query_args The current WP_Query arguments.
- * @param array $args Raw arguments passed to WC_Order_Data_Store_Custom_Table::get_orders().
- * @param WC_Order_Data_Store_Custom_Table $instance The current instance of the WC_Order_Data_Store_Custom_Table class.
- */
- $wp_query_args = apply_filters( 'woocommerce_order_data_store_cpt_get_orders_query', $wp_query_args, $args, $this );
- $orders = new WP_Query( $wp_query_args );
-
- if ( 'objects' === $args['return'] ) {
- $return = array_map( 'wc_get_order', $orders->posts );
- } else {
- $return = $orders->posts;
- }
-
- if ( $args['paginate'] ) {
- return (object) array(
- 'orders' => $return,
- 'total' => $orders->found_posts,
- 'max_num_pages' => $orders->max_num_pages,
- );
- } else {
- return $return;
- }
- }
-
- /**
- * Generate meta query for wc_get_orders.
- *
- * @param array $values Values to populate the meta query.
- * @param string $relation Optional The query relationship, either "and" or "or". Default is "or".
- *
- * @return array An array suitable for passing to WP_Query's meta_query argument.
- */
- private function get_orders_generate_customer_meta_query( $values, $relation = 'or' ) {
- $meta_query = array(
- 'relation' => strtoupper( $relation ),
- 'customer_emails' => array(
- 'key' => '_billing_email',
- 'value' => array(),
- 'compare' => 'IN',
- ),
- 'customer_ids' => array(
- 'key' => '_customer_user',
- 'value' => array(),
- 'compare' => 'IN',
- ),
- );
-
- foreach ( $values as $value ) {
- if ( is_array( $value ) ) {
- $meta_query[] = $this->get_orders_generate_customer_meta_query( $value, 'and' );
- } elseif ( is_email( $value ) ) {
- $meta_query['customer_emails']['value'][] = sanitize_email( $value );
- } else {
- $meta_query['customer_ids']['value'][] = strval( absint( $value ) );
- }
- }
-
- if ( empty( $meta_query['customer_emails']['value'] ) ) {
- unset( $meta_query['customer_emails'] );
- unset( $meta_query['relation'] );
- }
-
- if ( empty( $meta_query['customer_ids']['value'] ) ) {
- unset( $meta_query['customer_ids'] );
- unset( $meta_query['relation'] );
- }
-
- return $meta_query;
- }
-
- /**
- * Get unpaid orders after a certain date.
- *
- * @param int $date The Unix timestamp used for date filtering.
- *
- * @return array An array of unpaid orders.
- */
- public function get_unpaid_orders( $date ) {
- global $wpdb;
-
- $order_types = wc_get_order_types();
-
- return $wpdb->get_col( $wpdb->prepare(
- "SELECT ID
- FROM $wpdb->posts
- WHERE post_type IN (" . implode( ',', array_fill( 0, count( $order_types ), '%s' ) ) . ")
- AND post_status = 'wc-pending'
- AND post_modified < %s",
- array_merge( $order_types, array( date( 'Y-m-d H:i:s', (int) $date ) ) )
- ) );
+ ) ); // WPCS: DB call OK.
}
/**
@@ -554,162 +304,78 @@ public function get_unpaid_orders( $date ) {
public function search_orders( $term ) {
global $wpdb;
- $order_ids = array();
-
- // Treat a numeric search term as an order ID.
- if ( is_numeric( $term ) ) {
- $order_ids[] = absint( $term );
- }
-
- // Search given post meta columns for the query.
- $postmeta_search = array();
-
/**
* Searches on meta data can be slow - this lets you choose what fields to search.
+ * 3.0.0 added _billing_address and _shipping_address meta which contains all address data to make this faster.
+ * This however won't work on older orders unless updated, so search a few others (expand this using the filter if needed).
*
- * WooCommerce 2.7.0 added _billing_address and _shipping_address meta which contains all
- * address data to make this faster. However, this won't work on older orders unless they
- * are updated, so search a few others (expand this using the filter if needed).
+ * @var array
*/
- $meta_search_fields = array_map( 'wc_clean', apply_filters( 'woocommerce_shop_order_search_fields', array() ) );
-
- // If we were given meta fields to search, make it happen.
- if ( ! empty( $meta_search_fields ) ) {
- $postmeta_search = $wpdb->get_col( $wpdb->prepare( "
- SELECT DISTINCT post_id
- FROM {$wpdb->postmeta}
- WHERE meta_key IN (" . implode( ',', array_fill( 0, count( $meta_search_fields ), '%s' ) ) . ')
- AND meta_value LIKE %s
- ',
- array_merge( $meta_search_fields, array( '%' . $wpdb->esc_like( $term ) . '%' ) )
- ) );
- }
-
- return array_unique( array_merge(
- $order_ids,
- $postmeta_search,
- $wpdb->get_col(
- $wpdb->prepare( "
- SELECT order_id
- FROM {$wpdb->prefix}woocommerce_order_items as order_items
- WHERE order_item_name LIKE %s
- ",
- '%' . $wpdb->esc_like( $term ) . '%'
+ $search_fields = array_map(
+ 'wc_clean', apply_filters(
+ 'woocommerce_shop_order_search_fields', array(
+ '_billing_address_index',
+ '_shipping_address_index',
+ '_billing_last_name',
+ '_billing_email',
)
)
- ) );
- }
-
- /**
- * Gets information about whether permissions were generated yet.
- *
- * @param WC_Order|int $order The order object or ID.
- *
- * @return bool
- */
- public function get_download_permissions_granted( $order ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
-
- return wc_string_to_bool( get_post_meta( $order_id, '_download_permissions_granted', true ) );
- }
-
- /**
- * Stores information about whether permissions were generated yet.
- *
- * @param WC_Order|int $order The order object or ID.
- * @param bool $set Whether or not the permissions have been generated.
- */
- public function set_download_permissions_granted( $order, $set ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
-
- update_post_meta( $order_id, '_download_permissions_granted', wc_bool_to_string( $set ) );
- }
-
- /**
- * Gets information about whether sales were recorded.
- *
- * @param WC_Order|int $order The order object or ID.
- *
- * @return bool
- */
- public function get_recorded_sales( $order ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
-
- return wc_string_to_bool( get_post_meta( $order_id, '_recorded_sales', true ) );
- }
-
- /**
- * Stores information about whether sales were recorded.
- *
- * @param WC_Order|int $order The order object or ID.
- * @param bool $set Whether or not the sales have been recorded.
- */
- public function set_recorded_sales( $order, $set ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
-
- update_post_meta( $order_id, '_recorded_sales', wc_bool_to_string( $set ) );
- }
+ );
+ $term = wc_clean( $term );
+ $order_ids = array();
- /**
- * Gets information about whether coupon counts were updated.
- *
- * @param WC_Order|int $order The order object or ID.
- *
- * @return bool
- */
- public function get_recorded_coupon_usage_counts( $order ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
+ // Treat a numeric search term as an order ID.
+ if ( is_numeric( $term ) ) {
+ $order_ids[] = absint( $term );
+ }
- return wc_string_to_bool( get_post_meta( $order_id, '_recorded_coupon_usage_counts', true ) );
- }
+ // Search for order meta fields.
+ if ( ! empty( $search_fields ) ) {
+ $mapping = self::get_postmeta_mapping();
+ $in_table = array_intersect( $search_fields, $mapping );
+ $meta_keys = array_diff( $search_fields, $in_table );
+ $table = wc_custom_order_table()->get_table_name();
- /**
- * Stores information about whether coupon counts were updated.
- *
- * @param WC_Order|int $order The order object or ID.
- * @param bool $set Whether or not the coupon counts have been updated.
- */
- public function set_recorded_coupon_usage_counts( $order, $set ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
+ // Find results based on search fields that map to table columns.
+ if ( ! empty( $in_table ) ) {
+ $columns = array_keys( array_intersect( $mapping, $in_table ) );
+ $where = array();
- update_post_meta( $order_id, '_recorded_coupon_usage_counts', wc_bool_to_string( $set ) );
- }
+ foreach ( $columns as $column ) {
+ $where[] = "{$column} LIKE %s";
+ }
- /**
- * Gets information about whether stock was reduced.
- *
- * @param WC_Order|int $order The order object or ID.
- *
- * @return bool
- */
- public function get_stock_reduced( $order ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
+ $order_ids = array_merge( $order_ids, $wpdb->get_col( $wpdb->prepare(
+ 'SELECT DISTINCT order_id FROM ' . esc_sql( $table ) . ' WHERE ' . implode( ' OR ', $where ),
+ array_fill( 0, count( $where ), '%' . $wpdb->esc_like( $term ) . '%' )
+ ) ) ); // WPCS: DB call OK, Unprepared SQL ok, PreparedSQLPlaceholders replacement count ok.
+ }
- return wc_string_to_bool( get_post_meta( $order_id, '_order_stock_reduced', true ) );
- }
+ // For anything else, fall back to postmeta.
+ if ( ! empty( $meta_keys ) ) {
+ $order_ids = array_merge( $order_ids, $wpdb->get_col( $wpdb->prepare( "
+ SELECT DISTINCT post_id FROM {$wpdb->postmeta}
+ WHERE meta_value LIKE %s
+ AND meta_key IN (" . implode( ',', array_fill( 0, count( $meta_keys ), '%s' ) ) . ')',
+ array_merge(
+ array( '%' . $wpdb->esc_like( $term ) . '%' ),
+ $meta_keys
+ )
+ ) ) ); // WPCS: DB call OK.
+ }
+ }
- /**
- * Stores information about whether stock was reduced.
- *
- * @param WC_Order|int $order The order object or ID.
- *
- * @param bool $set Whether or not stock has been reduced.
- */
- public function set_stock_reduced( $order, $set ) {
- $order_id = WC_Order_Factory::get_order_id( $order );
+ // Search item names.
+ $order_ids = array_merge( $order_ids, $wpdb->get_col( $wpdb->prepare( "
+ SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items
+ WHERE order_item_name LIKE %s",
+ '%' . $wpdb->esc_like( $term ) . '%'
+ ) ) ); // WPCS: DB call OK.
- update_post_meta( $order_id, '_order_stock_reduced', wc_bool_to_string( $set ) );
- }
+ // Reduce the array of order IDs to unique values.
+ $order_ids = array_unique( $order_ids );
- /**
- * Get the order type based on Order ID.
- *
- * @param int $order_id The order ID.
- *
- * @return string The order post type.
- */
- public function get_order_type( $order_id ) {
- return get_post_type( $order_id );
+ return apply_filters( 'woocommerce_shop_order_search_results', $order_ids, $term, $search_fields );
}
/**
@@ -731,7 +397,7 @@ public function populate_from_meta( &$order, $save = true, $delete = false ) {
$this->creating = true;
}
- foreach ( $this->get_postmeta_mapping() as $column => $meta_key ) {
+ foreach ( self::get_postmeta_mapping() as $column => $meta_key ) {
$meta = get_post_meta( $order->get_id(), $meta_key, true );
if ( empty( $table_data->$column ) && ! empty( $meta ) ) {
@@ -751,7 +417,7 @@ public function populate_from_meta( &$order, $save = true, $delete = false ) {
}
if ( true === $delete ) {
- foreach ( $this->get_postmeta_mapping() as $column => $meta_key ) {
+ foreach ( self::get_postmeta_mapping() as $column => $meta_key ) {
delete_post_meta( $order->get_id(), $meta_key );
}
}
@@ -773,7 +439,7 @@ public function backfill_postmeta( &$order ) {
return;
}
- foreach ( $this->get_postmeta_mapping() as $column => $meta_key ) {
+ foreach ( self::get_postmeta_mapping() as $column => $meta_key ) {
if ( isset( $data->$column ) ) {
update_post_meta( $order->get_id(), $meta_key, $data->$column );
}
@@ -781,35 +447,127 @@ public function backfill_postmeta( &$order ) {
}
/**
- * Query for Orders matching specific criteria.
+ * Determine if any filters are required on the MySQL query and, if so, apply them.
*
- * @param array $query_vars Query arguments from a WC_Order_Query.
+ * @param array $query_args The arguments to be passed to WP_Query.
+ * @param array $query_vars The raw query vars passed to build the query.
*
- * @return array|object
+ * @return array The potentially-filtered $query_args array.
*/
- public function query( $query_vars ) {
- $args = $this->get_wp_query_args( $query_vars );
-
- if ( ! empty( $args['errors'] ) ) {
- $query = (object) array(
- 'posts' => array(),
- 'found_posts' => 0,
- 'max_num_pages' => 0,
- );
- } else {
- $query = new WP_Query( $args );
+ public static function filter_database_queries( $query_args, $query_vars ) {
+ $query_args['wc_order_meta_query'] = array();
+ $query_args['_wc_has_meta_columns'] = false;
+
+ // Iterate over the meta_query to find special cases.
+ if ( isset( $query_args['meta_query'] ) ) {
+ foreach ( $query_args['meta_query'] as $index => $meta_query ) {
+
+ // Flatten complex meta queries.
+ if ( is_array( $meta_query ) && 1 === count( $meta_query ) && is_array( current( $meta_query ) ) ) {
+ $meta_query = current( $meta_query );
+ }
+
+ if ( isset( $meta_query['customer_emails'] ) ) {
+ $query_args['wc_order_meta_query'][] = array_merge( $meta_query['customer_emails'], array(
+ 'key' => 'billing_email',
+ '_old_key' => $meta_query['customer_emails']['key'],
+ ) );
+ }
+
+ if ( isset( $meta_query['customer_ids'] ) ) {
+ $query_args['wc_order_meta_query'][] = array_merge( $meta_query['customer_ids'], array(
+ 'key' => 'customer_id',
+ '_old_key' => $meta_query['customer_ids']['key'],
+ ) );
+ }
+
+ if ( isset( $meta_query['key'] ) ) {
+ $column = array_search( $meta_query['key'], self::get_postmeta_mapping(), true );
+
+ if ( $column ) {
+ $query_args['wc_order_meta_query'][] = array_merge( $meta_query, array(
+ 'key' => $column,
+ '_old_key' => $meta_query['key'],
+ ) );
+ }
+ } else {
+ // Let this meta query pass through unaltered.
+ $query_args['_wc_has_meta_columns'] = true;
+ }
+ }
}
- $orders = ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) ? $query->posts : array_filter( array_map( 'wc_get_order', $query->posts ) );
+ // Add filters to address specific portions of the query.
+ add_filter( 'posts_join', __CLASS__ . '::posts_join', 10, 2 );
+ add_filter( 'posts_where', __CLASS__ . '::meta_query_where', 100, 2 );
- if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) {
- return (object) array(
- 'orders' => $orders,
- 'total' => $query->found_posts,
- 'max_num_pages' => $query->max_num_pages,
- );
+ return $query_args;
+ }
+
+ /**
+ * Filter the JOIN statement generated by WP_Query.
+ *
+ * @global $wpdb
+ *
+ * @param string $join The MySQL JOIN statement.
+ * @param WP_Query $wp_query The WP_Query object, passed by reference.
+ *
+ * @return string The filtered JOIN statement.
+ */
+ public static function posts_join( $join, $wp_query ) {
+ global $wpdb;
+
+ /*
+ * Remove the now-unnecessary INNER JOIN with the post_meta table unless there's some post
+ * meta that doesn't have a column in the custom table.
+ *
+ * @see WP_Meta_Query::get_sql_for_clause()
+ */
+ if ( ! $wp_query->get( '_wc_has_meta_columns', false ) ) {
+ // Match the post_meta table INNER JOIN, with or without an alias.
+ $regex = "/\sINNER\sJOIN\s{$wpdb->postmeta}\s+(AS\s[^\s]+)?\s*ON\s\([^\)]+\)/i";
+
+ $join = preg_replace( $regex, '', $join );
}
- return $orders;
+ $table = wc_custom_order_table()->get_table_name();
+ $join .= " LEFT JOIN {$table} ON ( {$wpdb->posts}.ID = {$table}.order_id ) ";
+
+ // Don't necessarily apply this to subsequent posts_join filter callbacks.
+ remove_filter( 'posts_join', __CLASS__ . '::posts_join', 10, 2 );
+
+ return $join;
+ }
+
+ /**
+ * Filter the "WHERE" portion of the MySQL query to look at the custom orders table instead of
+ * post meta.
+ *
+ * @global $wpdb
+ *
+ * @param string $where The MySQL WHERE statement.
+ * @param WP_Query $wp_query The WP_Query object, passed by reference.
+ *
+ * @return string The [potentially-] filtered WHERE statement.
+ */
+ public static function meta_query_where( $where, $wp_query ) {
+ global $wpdb;
+
+ $meta_query = $wp_query->get( 'wc_order_meta_query' );
+ $table = wc_custom_order_table()->get_table_name();
+
+ if ( empty( $meta_query ) ) {
+ return $where;
+ }
+
+ foreach ( $meta_query as $query ) {
+ $regex = $wpdb->prepare( '/\(\s?(\w+\.)?meta_key = %s AND (\w+\.)?meta_value /i', $query['_old_key'] );
+ $where = preg_replace( $regex, "( {$table}.{$query['key']} ", $where );
+ }
+
+ // Ensure this doesn't affect all subsequent queries.
+ remove_filter( 'posts_where', __CLASS__ . '::meta_query_where', 100, 2 );
+
+ return $where;
}
}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 56337d6..242bd78 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -2,8 +2,11 @@
Generally-applicable sniffs for WordPress plugins
-
-
+
+
+
+
+
@@ -12,7 +15,6 @@
- */node_modules/*
- */vendor/*
+
*/tests/*
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 5d2d5d1..58ff400 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,10 +8,16 @@
convertWarningsToExceptions="true"
>
-
+
+
./tests/
tests/test-sample.php
+
+
+
+ ./vendor/woocommerce/woocommerce/tests/unit-tests
+
diff --git a/bin/install-wp-tests.sh b/tests/bin/install-wp-tests.sh
similarity index 100%
rename from bin/install-wp-tests.sh
rename to tests/bin/install-wp-tests.sh
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 8951aba..c09a5ed 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,91 +1,39 @@
get_error_code() ) {
- include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+ WC_Custom_Order_Table_Install::activate();
- echo PHP_EOL . "WooCommerce is not currently installed in the test environment, attempting to install...";
-
- // Retrieve information about WooCommerce.
- $plugin_data = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/woocommerce.json' );
-
- if ( ! is_wp_error( $plugin_data ) ) {
- $plugin_data = json_decode( wp_remote_retrieve_body( $plugin_data ) );
- $plugin_url = $plugin_data->download_link;
- } else {
- $plugin_url = false;
- }
-
- // Download the plugin from the WordPress.org repository.
- $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
- $installed = $upgrader->install( $plugin_url );
-
- if ( true === $installed ) {
- echo "\033[0;32mOK\033[0;m" . PHP_EOL . PHP_EOL;
- } else {
- echo "\033[0;31mFAIL\033[0;m" . PHP_EOL;
-
- if ( is_wp_error( $installed ) ) {
- printf( 'Unable to install WooCommerce: %s.', $installed->get_error_message() );
- }
-
- printf(
- 'Please download and install WooCommerce into %s' . PHP_EOL,
- trailingslashit(dirname( dirname( $_tests_dir ) ) )
- );
-
- exit( 1 );
- }
-
- // Try once again to activate.
- $activated = activate_plugin( 'woocommerce/woocommerce.php' );
+ add_filter( 'woocommerce_email_actions', '__return_empty_array' );
}
+tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
-// Nothing more we can do, unfortunately.
-if ( is_wp_error( $activated ) ) {
- echo PHP_EOL . 'WooCommerce could not automatically be activated in the test environment:';
- echo PHP_EOL . $activated->get_error_message();
- echo PHP_EOL . PHP_EOL . "\033[0;31mUnable to proceed with tests, aborting.\033[0m";
- echo PHP_EOL;
- exit( 1 );
-}
+// Finally, Start up the WP testing environment.
+require_once $_bootstrap;
+require_once __DIR__ . '/testcase.php';
diff --git a/tests/test-bootstrap.php b/tests/test-bootstrap.php
index 700ba75..f7ec170 100644
--- a/tests/test-bootstrap.php
+++ b/tests/test-bootstrap.php
@@ -8,25 +8,11 @@
class BootstrapTest extends TestCase {
- /**
- * Tear down the plugin after each test run.
- *
- * @before
- */
- public function tear_down_plugin() {
- global $wc_custom_order_table;
-
- // Destroy the global $wc_custom_order_table instance.
- unset( $wc_custom_order_table );
- }
-
public function test_plugin_only_loads_after_woocommerce() {
global $wc_custom_order_table;
- $this->assertNull(
- $wc_custom_order_table,
- 'Before bootstrapping, $wc_custom_order_table should be empty.'
- );
+ // Test bootstrapping may have initialized it for us.
+ $wc_custom_order_table = null;
do_action( 'woocommerce_init' );
diff --git a/tests/test-data-store.php b/tests/test-data-store.php
index c313f7e..791efad 100644
--- a/tests/test-data-store.php
+++ b/tests/test-data-store.php
@@ -9,85 +9,73 @@
class DataStoreTest extends TestCase {
/**
- * Fire the necessary actions to bootstrap WordPress.
- *
- * @before
+ * @requires PHP 5.4 In order to support inline closures for hook callbacks.
*/
- public function init() {
- do_action( 'init' );
- }
-
- /**
- * Remove any closures that have been assigned to filters.
- *
- * @after
- */
- public function remove_filter_callbacks() {
- remove_all_filters( 'woocommerce_shop_order_search_fields' );
- }
-
public function test_create() {
- $instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create_and_get();
- $order_key = 'my_custom_order_key';
-
- add_filter( 'woocommerce_generate_order_key', function () use ( $order_key ) {
- return $order_key;
+ $instance = new WC_Order_Data_Store_Custom_Table();
+ $property = new ReflectionProperty( $instance, 'creating' );
+ $property->setAccessible( true );
+ $order = new WC_Order( wp_insert_post( array(
+ 'post_type' => 'product',
+ ) ) );
+
+ add_action( 'wp_insert_post', function () use ( $property, $instance ) {
+ $this->assertTrue(
+ $property->getValue( $instance ),
+ 'As an order is being created, WC_Order_Data_Store_Custom_Table::$creating should be true'
+ );
} );
$instance->create( $order );
- $this->assertEquals( 'wc_' . $order_key, $order->get_order_key() );
+ $this->assertEquals( 1, did_action( 'wp_insert_post' ), 'Expected the "wp_insert_post" action to have been fired.' );
}
- public function test_get_order_count() {
+ public function test_delete() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $orders = $this->factory()->order->create_many( 5, array(
- 'post_status' => 'wc-pending',
- ) );
+ $order = WC_Helper_Order::create_order();
- $this->assertEquals(
- count( $orders ),
- $instance->get_order_count( 'wc-pending' )
+ $instance->delete( $order, array( 'force_delete' => false ) );
+
+ $this->assertNotNull(
+ $this->get_order_row( $order->get_id() ),
+ 'Unless force_delete is true, the table row should not be removed.'
);
}
- public function test_get_order_count_filters_by_status() {
+ public function test_delete_can_force_delete() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $this->factory()->order->create( array(
- 'post_status' => 'not_a_pending_status',
- ) );
+ $order = WC_Helper_Order::create_order();
+ $order_id = $order->get_id();
- $this->assertEquals(
- 0,
- $instance->get_order_count( 'wc-pending' ),
- 'The get_order_count() method should only count records matching $status.'
- );
+ $instance->delete( $order, array( 'force_delete' => true ) );
+
+ $this->assertNull( $this->get_order_row( $order_id ), 'When force deleting, the table row should be removed.' );
}
- public function test_get_unpaid_orders() {
- $instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create( array(
- 'post_status' => 'wc-pending',
- ) );
- $pending = $instance->get_unpaid_orders( time() + DAY_IN_SECONDS );
+ public function test_update_post_meta_for_new_order() {
+ $order = new WC_Order( wp_insert_post( array(
+ 'post_type' => 'product',
+ ) ) );
+ $order->set_currency( 'USD' );
+ $order->set_prices_include_tax( false );
+ $order->set_customer_ip_address( '127.0.0.1' );
+ $order->set_customer_user_agent( 'PHPUnit' );
- $this->assertCount( 1, $pending, 'There should be only one unpaid order.' );
- $this->assertEquals(
- $order,
- array_shift( $pending ),
- 'The ID of the one unpaid order should be that of $order.'
- );
+ $this->invoke_update_post_meta( $order, true );
+
+ $row = $this->get_order_row( $order->get_id() );
+
+ $this->assertEquals( 'USD', $row['currency'] );
+ $this->assertEquals( '127.0.0.1', $row['customer_ip_address'] );
+ $this->assertEquals( 'PHPUnit', $row['customer_user_agent'] );
}
- public function test_get_unpaid_orders_uses_date_filtering() {
+ public function test_get_order_id_by_order_key() {
+ $order = WC_Helper_Order::create_order();
$instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create( array(
- 'post_status' => 'wc-pending',
- ) );
- $pending = $instance->get_unpaid_orders( time() - HOUR_IN_SECONDS );
- $this->assertEmpty( $pending, 'No unpaid orders should match the time window.' );
+ $this->assertEquals( $order->get_id(), $instance->get_order_id_by_order_key( $order->get_order_key() ) );
}
public function test_search_orders_can_search_by_order_id() {
@@ -102,20 +90,29 @@ public function test_search_orders_can_search_by_order_id() {
public function test_search_orders_can_check_post_meta() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create();
+ $order = WC_Helper_Order::create_order();
$term = uniqid( 'search term ' );
- add_post_meta( $order, 'some_custom_meta_key', $term );
+ add_post_meta( $order->get_id(), 'some_custom_meta_key', $term );
- add_filter( 'woocommerce_shop_order_search_fields', function () {
- return array( 'some_custom_meta_key' );
- } );
+ add_filter( 'woocommerce_shop_order_search_fields', __CLASS__ . '::return_array_for_test_search_orders_can_check_post_meta' );
$this->assertEquals(
- array( $order ),
+ array( $order->get_id() ),
$instance->search_orders( $term ),
'If post meta keys are specified, they should also be searched.'
);
+
+ remove_filter( 'woocommerce_shop_order_search_fields', __CLASS__ . '::return_array_for_test_search_orders_can_check_post_meta' );
+ }
+
+ /**
+ * Filter callback for test_search_orders_can_check_post_meta().
+ *
+ * Can be dropped once PHP 5.3 isn't a requirement, as closures are far nicer.
+ */
+ public static function return_array_for_test_search_orders_can_check_post_meta() {
+ return array( 'some_custom_meta_key' );
}
/**
@@ -123,10 +120,10 @@ public function test_search_orders_can_check_post_meta() {
*/
public function test_search_orders_only_checks_post_meta_if_specified() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create();
+ $order = WC_Helper_Order::create_order();
$term = uniqid( 'search term ' );
- add_post_meta( $order, 'some_custom_meta_key', $term );
+ add_post_meta( $order->get_id(), 'some_custom_meta_key', $term );
$this->assertEmpty(
$instance->search_orders( $term ),
@@ -136,8 +133,8 @@ public function test_search_orders_only_checks_post_meta_if_specified() {
public function test_search_orders_checks_table_for_product_item_matches() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $product = $this->factory()->product->create_and_get();
- $order = $this->factory()->order->create_and_get();
+ $product = WC_Helper_Product::create_simple_product();
+ $order = WC_Helper_Order::create_order();
$order->add_product( $product );
$order->save();
@@ -150,10 +147,10 @@ public function test_search_orders_checks_table_for_product_item_matches() {
public function test_search_orders_checks_table_for_product_item_matches_with_like_comparison() {
$instance = new WC_Order_Data_Store_Custom_Table();
- $product = $this->factory()->product->create_and_get( array(
- 'post_title' => 'foo bar baz',
- ) );
- $order = $this->factory()->order->create_and_get();
+ $product = WC_Helper_Product::create_simple_product();
+ $product->set_name( 'Foo Bar Baz' );
+ $product->save();
+ $order = WC_Helper_Order::create_order();
$order->add_product( $product );
$order->save();
@@ -165,30 +162,21 @@ public function test_search_orders_checks_table_for_product_item_matches_with_li
}
/**
- * @dataProvider order_type_provider()
+ * Shortcut for setting up reflection methods + properties for update_post_meta().
+ *
+ * @param WC_Order $order The order object, passed by reference.
+ * @param bool $creating Optional. The value 'creating' property in the new instance.
+ * Default is false.
*/
- public function test_get_order_type( $order_type ) {
+ protected function invoke_update_post_meta( &$order, $creating = false ) {
$instance = new WC_Order_Data_Store_Custom_Table();
- $order = $this->factory()->order->create( array(
- 'post_type' => $order_type,
- ) );
-
- $this->assertEquals(
- $order_type,
- $instance->get_order_type( $order )
- );
- }
-
- /**
- * Provide a list of all available order types.
- */
- public function order_type_provider() {
- $types = array();
- foreach ( wc_get_order_types() as $type ) {
- $types[ $type ] = array( $type );
- }
+ $property = new ReflectionProperty( $instance, 'creating' );
+ $property->setAccessible( true );
+ $property->setValue( $instance, (bool) $creating );
- return $types;
+ $method = new ReflectionMethod( $instance, 'update_post_meta' );
+ $method->setAccessible( true );
+ $method->invokeArgs( $instance, array( &$order ) );
}
}
diff --git a/tests/test-tools/class-wp-unittest-factory-for-customers.php b/tests/test-tools/class-wp-unittest-factory-for-customers.php
deleted file mode 100644
index 3cb8df7..0000000
--- a/tests/test-tools/class-wp-unittest-factory-for-customers.php
+++ /dev/null
@@ -1,27 +0,0 @@
-default_generation_definitions = array(
- 'user_login' => new WP_UnitTest_Generator_Sequence( 'Customer %s' ),
- 'user_pass' => 'password',
- 'user_email' => new WP_UnitTest_Generator_Sequence( 'customer_%s@example.org' ),
- );
- }
-
- function get_object_by_id( $user_id ) {
- return new WC_Customer( $user_id );
- }
-}
diff --git a/tests/test-tools/class-wp-unittest-factory-for-order.php b/tests/test-tools/class-wp-unittest-factory-for-order.php
deleted file mode 100644
index 4758661..0000000
--- a/tests/test-tools/class-wp-unittest-factory-for-order.php
+++ /dev/null
@@ -1,28 +0,0 @@
-default_generation_definitions = array(
- 'post_status' => 'wc-pending',
- 'post_title' => new WP_UnitTest_Generator_Sequence( 'Order %s' ),
- 'post_password' => uniqid( 'wc_' ),
- 'post_type' => 'shop_order',
- );
- }
-
- function get_object_by_id( $order_id ) {
- return wc_get_order( $order_id );
- }
-}
diff --git a/tests/test-tools/class-wp-unittest-factory-for-product.php b/tests/test-tools/class-wp-unittest-factory-for-product.php
deleted file mode 100644
index 9edaccc..0000000
--- a/tests/test-tools/class-wp-unittest-factory-for-product.php
+++ /dev/null
@@ -1,29 +0,0 @@
-default_generation_definitions = array(
- 'post_status' => 'publish',
- 'post_title' => new WP_UnitTest_Generator_Sequence( 'Product name %s' ),
- 'post_content' => new WP_UnitTest_Generator_Sequence( 'Product description %s' ),
- 'post_excerpt' => new WP_UnitTest_Generator_Sequence( 'Product short description %s' ),
- 'post_type' => 'product',
- );
- }
-
- function get_object_by_id( $product_id ) {
- return wc_get_product( $product_id );
- }
-}
diff --git a/tests/testcase.php b/tests/testcase.php
index 1b1eb4c..575305e 100644
--- a/tests/testcase.php
+++ b/tests/testcase.php
@@ -6,29 +6,37 @@
* @author Liquid Web
*/
-class TestCase extends WP_UnitTestCase {
+class TestCase extends WC_Unit_Test_Case {
/**
- * Retrieve the core test suite's factory object, but add extra factories.
+ * Delete all data from the orders table after each test.
*
- * @return WP_UnitTest_Factory
+ * @after
+ *
+ * @global $wpdb
*/
- protected static function factory() {
- static $factory = null;
-
- if ( ! $factory ) {
- $instance = new WP_UnitTest_Factory();
+ function truncate_table() {
+ global $wpdb;
- // Add additional factories.
- $instance->customer = new WP_UnitTest_Factory_For_Customer( $instance );
- $instance->order = new WP_UnitTest_Factory_For_Order( $instance );
- $instance->product = new WP_UnitTest_Factory_For_Product( $instance );
+ $wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_orders" );
+ }
- // Save the instance in the static $factory variable.
- $factory = $instance;
- }
+ /**
+ * Retrieve a single row from the Orders table.
+ *
+ * @global $wpdb
+ *
+ * @param int $order_id The order ID to retrieve.
+ *
+ * @return array|null The contents of the database row or null if the given row doesn't exist.
+ */
+ protected function get_order_row( $order_id ) {
+ global $wpdb;
- return $factory;
+ return $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$wpdb->prefix}woocommerce_orders WHERE order_id = %d",
+ $order_id
+ ), ARRAY_A );
}
/**
diff --git a/wc-custom-order-table.php b/wc-custom-order-table.php
index c16be92..bffa17b 100644
--- a/wc-custom-order-table.php
+++ b/wc-custom-order-table.php
@@ -22,7 +22,7 @@
/* Load includes via a PHP 5.2-compatible autoloader. */
if ( file_exists( WC_CUSTOM_ORDER_TABLE_PATH . 'vendor/autoload_52.php' ) ) {
- require( WC_CUSTOM_ORDER_TABLE_PATH . 'vendor/autoload_52.php' );
+ require WC_CUSTOM_ORDER_TABLE_PATH . 'vendor/autoload_52.php';
}
/**
@@ -43,7 +43,7 @@ function wc_custom_order_table() {
global $wc_custom_order_table;
if ( ! $wc_custom_order_table instanceof WC_Custom_Order_Table ) {
- $wc_custom_order_table = new WC_Custom_Order_Table;
+ $wc_custom_order_table = new WC_Custom_Order_Table();
$wc_custom_order_table->setup();
}