Edit File by line
/home/zeestwma/richards.../wp-conte.../plugins/woocomme.../src/Admin/API/Reports
File: DataStore.php
<?php
[0] Fix | Delete
/**
[1] Fix | Delete
* Admin\API\Reports\DataStore class file.
[2] Fix | Delete
*/
[3] Fix | Delete
[4] Fix | Delete
namespace Automattic\WooCommerce\Admin\API\Reports;
[5] Fix | Delete
[6] Fix | Delete
if ( ! defined( 'ABSPATH' ) ) {
[7] Fix | Delete
exit;
[8] Fix | Delete
}
[9] Fix | Delete
[10] Fix | Delete
use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
[11] Fix | Delete
use Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
[12] Fix | Delete
[13] Fix | Delete
/**
[14] Fix | Delete
* Common parent for custom report data stores.
[15] Fix | Delete
*
[16] Fix | Delete
* We use Report DataStores to separate DB data retrieval logic from the REST API controllers.
[17] Fix | Delete
*
[18] Fix | Delete
* Handles caching, data normalization, intervals-related methods, and other common functionality.
[19] Fix | Delete
* So, in your custom report DataStore class that extends this class
[20] Fix | Delete
* you can focus on specifics by overriding the `get_noncached_data` method.
[21] Fix | Delete
*
[22] Fix | Delete
* Minimalistic example:
[23] Fix | Delete
* <pre><code class="language-php">class MyDataStore extends DataStore implements DataStoreInterface {
[24] Fix | Delete
* /** Cache identifier, used by the `DataStore` class to handle caching for you. &ast;/
[25] Fix | Delete
* protected $cache_key = 'my_thing';
[26] Fix | Delete
* /** Data store context used to pass to filters. &ast;/
[27] Fix | Delete
* protected $context = 'my_thing';
[28] Fix | Delete
* /** Table used to get the data. &ast;/
[29] Fix | Delete
* protected static $table_name = 'my_table';
[30] Fix | Delete
* /**
[31] Fix | Delete
* * Method that overrides the `DataStore::get_noncached_data()` to return the report data.
[32] Fix | Delete
* * Will be called by `get_data` if there is no data in cache.
[33] Fix | Delete
* &ast;/
[34] Fix | Delete
* public function get_noncached_data( $query ) {
[35] Fix | Delete
* // Do your magic.
[36] Fix | Delete
*
[37] Fix | Delete
* // Then return your data in conforming object structure.
[38] Fix | Delete
* return (object) array(
[39] Fix | Delete
* 'data' => $product_data,
[40] Fix | Delete
* 'total' => 1,
[41] Fix | Delete
* 'page_no' => 1,
[42] Fix | Delete
* 'pages' => 1,
[43] Fix | Delete
* );
[44] Fix | Delete
* }
[45] Fix | Delete
* }
[46] Fix | Delete
* </code></pre>
[47] Fix | Delete
*
[48] Fix | Delete
* Please use the `woocommerce_data_stores` filter to add your custom data store to the list of available ones.
[49] Fix | Delete
* Then, your store could be accessed by Controller classes ({@see GenericController::get_datastore_data() GenericController::get_datastore_data()})
[50] Fix | Delete
* or using {@link \WC_Data_Store::load() \WC_Data_Store::load()}.
[51] Fix | Delete
*
[52] Fix | Delete
* We recommend registering using the REST base name of your Controller as the key, e.g.:
[53] Fix | Delete
* <pre><code class="language-php">add_filter( 'woocommerce_data_stores', function( $stores ) {
[54] Fix | Delete
* $stores['reports/my-thing'] = 'MyExtension\Admin\Analytics\Rest_API\MyDataStore';
[55] Fix | Delete
* } );
[56] Fix | Delete
* </code></pre>
[57] Fix | Delete
* This way, `GenericController` will pick it up automatically.
[58] Fix | Delete
*
[59] Fix | Delete
* Note that this class is NOT {@link https://developer.woocommerce.com/docs/how-to-manage-woocommerce-data-stores/ a CRUD data store}.
[60] Fix | Delete
* It does not implement the {@see WC_Object_Data_Store_Interface WC_Object_Data_Store_Interface} nor extend WC_Data & WC_Data_Store_WP classes.
[61] Fix | Delete
*/
[62] Fix | Delete
class DataStore extends SqlQuery implements DataStoreInterface {
[63] Fix | Delete
[64] Fix | Delete
/**
[65] Fix | Delete
* Cache group for the reports.
[66] Fix | Delete
*
[67] Fix | Delete
* @var string
[68] Fix | Delete
*/
[69] Fix | Delete
protected $cache_group = 'reports';
[70] Fix | Delete
[71] Fix | Delete
/**
[72] Fix | Delete
* Time out for the cache.
[73] Fix | Delete
*
[74] Fix | Delete
* @var int
[75] Fix | Delete
*/
[76] Fix | Delete
protected $cache_timeout = 3600;
[77] Fix | Delete
[78] Fix | Delete
/**
[79] Fix | Delete
* Cache identifier.
[80] Fix | Delete
*
[81] Fix | Delete
* @var string
[82] Fix | Delete
*/
[83] Fix | Delete
protected $cache_key = '';
[84] Fix | Delete
[85] Fix | Delete
/**
[86] Fix | Delete
* Table used as a data store for this report.
[87] Fix | Delete
*
[88] Fix | Delete
* @var string
[89] Fix | Delete
*/
[90] Fix | Delete
protected static $table_name = '';
[91] Fix | Delete
[92] Fix | Delete
/**
[93] Fix | Delete
* Date field name.
[94] Fix | Delete
*
[95] Fix | Delete
* @var string
[96] Fix | Delete
*/
[97] Fix | Delete
protected $date_column_name = 'date_created';
[98] Fix | Delete
[99] Fix | Delete
/**
[100] Fix | Delete
* Mapping columns to data type to return correct response types.
[101] Fix | Delete
*
[102] Fix | Delete
* @var array
[103] Fix | Delete
*/
[104] Fix | Delete
protected $column_types = array();
[105] Fix | Delete
[106] Fix | Delete
/**
[107] Fix | Delete
* SQL columns to select in the db query.
[108] Fix | Delete
*
[109] Fix | Delete
* @var array
[110] Fix | Delete
*/
[111] Fix | Delete
protected $report_columns = array();
[112] Fix | Delete
[113] Fix | Delete
// @todo This does not really belong here, maybe factor out the comparison as separate class?
[114] Fix | Delete
/**
[115] Fix | Delete
* Order by property, used in the cmp function.
[116] Fix | Delete
*
[117] Fix | Delete
* @var string
[118] Fix | Delete
*/
[119] Fix | Delete
private $order_by = '';
[120] Fix | Delete
[121] Fix | Delete
/**
[122] Fix | Delete
* Order property, used in the cmp function.
[123] Fix | Delete
*
[124] Fix | Delete
* @var string
[125] Fix | Delete
*/
[126] Fix | Delete
private $order = '';
[127] Fix | Delete
[128] Fix | Delete
/**
[129] Fix | Delete
* Query limit parameters.
[130] Fix | Delete
*
[131] Fix | Delete
* @var array
[132] Fix | Delete
*/
[133] Fix | Delete
private $limit_parameters = array();
[134] Fix | Delete
[135] Fix | Delete
/**
[136] Fix | Delete
* Data store context used to pass to filters.
[137] Fix | Delete
*
[138] Fix | Delete
* @override SqlQuery
[139] Fix | Delete
*
[140] Fix | Delete
* @var string
[141] Fix | Delete
*/
[142] Fix | Delete
protected $context = 'reports';
[143] Fix | Delete
[144] Fix | Delete
/**
[145] Fix | Delete
* Subquery object for query nesting.
[146] Fix | Delete
*
[147] Fix | Delete
* @var SqlQuery
[148] Fix | Delete
*/
[149] Fix | Delete
protected $subquery;
[150] Fix | Delete
[151] Fix | Delete
/**
[152] Fix | Delete
* Totals query object.
[153] Fix | Delete
*
[154] Fix | Delete
* @var SqlQuery
[155] Fix | Delete
*/
[156] Fix | Delete
protected $total_query;
[157] Fix | Delete
[158] Fix | Delete
/**
[159] Fix | Delete
* Intervals query object.
[160] Fix | Delete
*
[161] Fix | Delete
* @var SqlQuery
[162] Fix | Delete
*/
[163] Fix | Delete
protected $interval_query;
[164] Fix | Delete
[165] Fix | Delete
/**
[166] Fix | Delete
* Refresh the cache for the current query when true.
[167] Fix | Delete
*
[168] Fix | Delete
* @var bool
[169] Fix | Delete
*/
[170] Fix | Delete
protected $force_cache_refresh = false;
[171] Fix | Delete
[172] Fix | Delete
/**
[173] Fix | Delete
* Include debugging information in the returned data when true.
[174] Fix | Delete
*
[175] Fix | Delete
* @var bool
[176] Fix | Delete
*/
[177] Fix | Delete
protected $debug_cache = true;
[178] Fix | Delete
[179] Fix | Delete
/**
[180] Fix | Delete
* Debugging information to include in the returned data.
[181] Fix | Delete
*
[182] Fix | Delete
* @var array
[183] Fix | Delete
*/
[184] Fix | Delete
protected $debug_cache_data = array();
[185] Fix | Delete
[186] Fix | Delete
/**
[187] Fix | Delete
* Class constructor.
[188] Fix | Delete
*
[189] Fix | Delete
* @override SqlQuery::__construct()
[190] Fix | Delete
*/
[191] Fix | Delete
public function __construct() {
[192] Fix | Delete
self::set_db_table_name();
[193] Fix | Delete
$this->assign_report_columns();
[194] Fix | Delete
[195] Fix | Delete
if ( $this->report_columns ) {
[196] Fix | Delete
$this->report_columns = apply_filters(
[197] Fix | Delete
'woocommerce_admin_report_columns',
[198] Fix | Delete
$this->report_columns,
[199] Fix | Delete
$this->context,
[200] Fix | Delete
self::get_db_table_name()
[201] Fix | Delete
);
[202] Fix | Delete
}
[203] Fix | Delete
[204] Fix | Delete
// Utilize enveloped responses to include debugging info.
[205] Fix | Delete
// See https://querymonitor.com/blog/2021/05/debugging-wordpress-rest-api-requests/
[206] Fix | Delete
if ( isset( $_GET['_envelope'] ) ) {
[207] Fix | Delete
$this->debug_cache = true;
[208] Fix | Delete
add_filter( 'rest_envelope_response', array( $this, 'add_debug_cache_to_envelope' ), 999, 2 );
[209] Fix | Delete
}
[210] Fix | Delete
}
[211] Fix | Delete
[212] Fix | Delete
[213] Fix | Delete
/**
[214] Fix | Delete
* Get the data based on args.
[215] Fix | Delete
*
[216] Fix | Delete
* Returns the report data based on parameters supplied by the user.
[217] Fix | Delete
* Fetches it from cache or returns `get_noncached_data` result.
[218] Fix | Delete
*
[219] Fix | Delete
* @param array $query_args Query parameters.
[220] Fix | Delete
* @return stdClass|WP_Error
[221] Fix | Delete
*/
[222] Fix | Delete
public function get_data( $query_args ) {
[223] Fix | Delete
$defaults = $this->get_default_query_vars();
[224] Fix | Delete
$query_args = wp_parse_args( $query_args, $defaults );
[225] Fix | Delete
$this->normalize_timezones( $query_args, $defaults );
[226] Fix | Delete
[227] Fix | Delete
/*
[228] Fix | Delete
* We need to get the cache key here because
[229] Fix | Delete
* parent::update_intervals_sql_params() modifies $query_args.
[230] Fix | Delete
*/
[231] Fix | Delete
$cache_key = $this->get_cache_key( $query_args );
[232] Fix | Delete
$data = $this->get_cached_data( $cache_key );
[233] Fix | Delete
[234] Fix | Delete
if ( false === $data ) {
[235] Fix | Delete
$data = $this->get_noncached_data( $query_args );
[236] Fix | Delete
$this->set_cached_data( $cache_key, $data );
[237] Fix | Delete
}
[238] Fix | Delete
[239] Fix | Delete
return $data;
[240] Fix | Delete
}
[241] Fix | Delete
[242] Fix | Delete
/**
[243] Fix | Delete
* Get the default query arguments to be used by get_data().
[244] Fix | Delete
* These defaults are only partially applied when used via REST API, as that has its own defaults.
[245] Fix | Delete
*
[246] Fix | Delete
* @return array Query parameters.
[247] Fix | Delete
*/
[248] Fix | Delete
public function get_default_query_vars() {
[249] Fix | Delete
return array(
[250] Fix | Delete
'per_page' => get_option( 'posts_per_page' ),
[251] Fix | Delete
'page' => 1,
[252] Fix | Delete
'order' => 'DESC',
[253] Fix | Delete
'orderby' => 'date',
[254] Fix | Delete
'before' => TimeInterval::default_before(),
[255] Fix | Delete
'after' => TimeInterval::default_after(),
[256] Fix | Delete
'fields' => '*',
[257] Fix | Delete
);
[258] Fix | Delete
}
[259] Fix | Delete
[260] Fix | Delete
/**
[261] Fix | Delete
* Get table name from database class.
[262] Fix | Delete
*/
[263] Fix | Delete
public static function get_db_table_name() {
[264] Fix | Delete
global $wpdb;
[265] Fix | Delete
return isset( $wpdb->{static::$table_name} ) ? $wpdb->{static::$table_name} : $wpdb->prefix . static::$table_name;
[266] Fix | Delete
}
[267] Fix | Delete
[268] Fix | Delete
/**
[269] Fix | Delete
* Returns the report data based on normalized parameters.
[270] Fix | Delete
* Will be called by `get_data` if there is no data in cache.
[271] Fix | Delete
*
[272] Fix | Delete
* @see get_data
[273] Fix | Delete
* @param array $query_args Query parameters.
[274] Fix | Delete
* @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error.
[275] Fix | Delete
*/
[276] Fix | Delete
public function get_noncached_data( $query_args ) {
[277] Fix | Delete
/* translators: %s: Method name */
[278] Fix | Delete
return new \WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) );
[279] Fix | Delete
}
[280] Fix | Delete
[281] Fix | Delete
/**
[282] Fix | Delete
* Set table name from database class.
[283] Fix | Delete
*/
[284] Fix | Delete
protected static function set_db_table_name() {
[285] Fix | Delete
global $wpdb;
[286] Fix | Delete
if ( static::$table_name && ! isset( $wpdb->{static::$table_name} ) ) {
[287] Fix | Delete
$wpdb->{static::$table_name} = $wpdb->prefix . static::$table_name;
[288] Fix | Delete
}
[289] Fix | Delete
}
[290] Fix | Delete
[291] Fix | Delete
/**
[292] Fix | Delete
* Whether or not the report should use the caching layer.
[293] Fix | Delete
*
[294] Fix | Delete
* Provides an opportunity for plugins to prevent reports from using cache.
[295] Fix | Delete
*
[296] Fix | Delete
* @return boolean Whether or not to utilize caching.
[297] Fix | Delete
*/
[298] Fix | Delete
protected function should_use_cache() {
[299] Fix | Delete
/**
[300] Fix | Delete
* Determines if a report will utilize caching.
[301] Fix | Delete
*
[302] Fix | Delete
* @param bool $use_cache Whether or not to use cache.
[303] Fix | Delete
* @param string $cache_key The report's cache key. Used to identify the report.
[304] Fix | Delete
*/
[305] Fix | Delete
return (bool) apply_filters( 'woocommerce_analytics_report_should_use_cache', true, $this->cache_key );
[306] Fix | Delete
}
[307] Fix | Delete
[308] Fix | Delete
/**
[309] Fix | Delete
* Returns string to be used as cache key for the data.
[310] Fix | Delete
*
[311] Fix | Delete
* @param array $params Query parameters.
[312] Fix | Delete
* @return string
[313] Fix | Delete
*/
[314] Fix | Delete
protected function get_cache_key( $params ) {
[315] Fix | Delete
if ( isset( $params['force_cache_refresh'] ) ) {
[316] Fix | Delete
if ( true === $params['force_cache_refresh'] ) {
[317] Fix | Delete
$this->force_cache_refresh = true;
[318] Fix | Delete
}
[319] Fix | Delete
[320] Fix | Delete
// We don't want this param in the key.
[321] Fix | Delete
unset( $params['force_cache_refresh'] );
[322] Fix | Delete
}
[323] Fix | Delete
[324] Fix | Delete
if ( true === $this->debug_cache ) {
[325] Fix | Delete
$this->debug_cache_data['query_args'] = $params;
[326] Fix | Delete
}
[327] Fix | Delete
[328] Fix | Delete
return implode(
[329] Fix | Delete
'_',
[330] Fix | Delete
array(
[331] Fix | Delete
'wc_report',
[332] Fix | Delete
$this->cache_key,
[333] Fix | Delete
md5( wp_json_encode( $params ) ),
[334] Fix | Delete
)
[335] Fix | Delete
);
[336] Fix | Delete
}
[337] Fix | Delete
[338] Fix | Delete
/**
[339] Fix | Delete
* Wrapper around Cache::get().
[340] Fix | Delete
*
[341] Fix | Delete
* @param string $cache_key Cache key.
[342] Fix | Delete
* @return mixed
[343] Fix | Delete
*/
[344] Fix | Delete
protected function get_cached_data( $cache_key ) {
[345] Fix | Delete
if ( true === $this->debug_cache ) {
[346] Fix | Delete
$this->debug_cache_data['should_use_cache'] = $this->should_use_cache();
[347] Fix | Delete
$this->debug_cache_data['force_cache_refresh'] = $this->force_cache_refresh;
[348] Fix | Delete
$this->debug_cache_data['cache_hit'] = false;
[349] Fix | Delete
}
[350] Fix | Delete
[351] Fix | Delete
if ( $this->should_use_cache() && false === $this->force_cache_refresh ) {
[352] Fix | Delete
$cached_data = Cache::get( $cache_key );
[353] Fix | Delete
[354] Fix | Delete
$cache_hit = false !== $cached_data;
[355] Fix | Delete
if ( true === $this->debug_cache ) {
[356] Fix | Delete
$this->debug_cache_data['cache_hit'] = $cache_hit;
[357] Fix | Delete
}
[358] Fix | Delete
[359] Fix | Delete
return $cached_data;
[360] Fix | Delete
}
[361] Fix | Delete
[362] Fix | Delete
// Cached item has now functionally been refreshed. Reset the option.
[363] Fix | Delete
$this->force_cache_refresh = false;
[364] Fix | Delete
[365] Fix | Delete
return false;
[366] Fix | Delete
}
[367] Fix | Delete
[368] Fix | Delete
/**
[369] Fix | Delete
* Wrapper around Cache::set().
[370] Fix | Delete
*
[371] Fix | Delete
* @param string $cache_key Cache key.
[372] Fix | Delete
* @param mixed $value New value.
[373] Fix | Delete
* @return bool
[374] Fix | Delete
*/
[375] Fix | Delete
protected function set_cached_data( $cache_key, $value ) {
[376] Fix | Delete
if ( $this->should_use_cache() ) {
[377] Fix | Delete
return Cache::set( $cache_key, $value );
[378] Fix | Delete
}
[379] Fix | Delete
[380] Fix | Delete
return true;
[381] Fix | Delete
}
[382] Fix | Delete
[383] Fix | Delete
/**
[384] Fix | Delete
* Add cache debugging information to an enveloped API response.
[385] Fix | Delete
*
[386] Fix | Delete
* @param array $envelope
[387] Fix | Delete
* @param \WP_REST_Response $response
[388] Fix | Delete
*
[389] Fix | Delete
* @return array
[390] Fix | Delete
*/
[391] Fix | Delete
public function add_debug_cache_to_envelope( $envelope, $response ) {
[392] Fix | Delete
if ( 0 !== strncmp( '/wc-analytics', $response->get_matched_route(), 13 ) ) {
[393] Fix | Delete
return $envelope;
[394] Fix | Delete
}
[395] Fix | Delete
[396] Fix | Delete
if ( ! empty( $this->debug_cache_data ) ) {
[397] Fix | Delete
$envelope['debug_cache'] = $this->debug_cache_data;
[398] Fix | Delete
}
[399] Fix | Delete
[400] Fix | Delete
return $envelope;
[401] Fix | Delete
}
[402] Fix | Delete
[403] Fix | Delete
/**
[404] Fix | Delete
* Compares two report data objects by pre-defined object property and ASC/DESC ordering.
[405] Fix | Delete
*
[406] Fix | Delete
* @param stdClass $a Object a.
[407] Fix | Delete
* @param stdClass $b Object b.
[408] Fix | Delete
* @return string
[409] Fix | Delete
*/
[410] Fix | Delete
private function interval_cmp( $a, $b ) {
[411] Fix | Delete
if ( '' === $this->order_by || '' === $this->order ) {
[412] Fix | Delete
return 0;
[413] Fix | Delete
// @todo Should return WP_Error here perhaps?
[414] Fix | Delete
}
[415] Fix | Delete
if ( $a[ $this->order_by ] === $b[ $this->order_by ] ) {
[416] Fix | Delete
// As relative order is undefined in case of equality in usort, second-level sorting by date needs to be enforced
[417] Fix | Delete
// so that paging is stable.
[418] Fix | Delete
if ( $a['time_interval'] === $b['time_interval'] ) {
[419] Fix | Delete
return 0; // This should never happen.
[420] Fix | Delete
} elseif ( $a['time_interval'] > $b['time_interval'] ) {
[421] Fix | Delete
return 1;
[422] Fix | Delete
} elseif ( $a['time_interval'] < $b['time_interval'] ) {
[423] Fix | Delete
return -1;
[424] Fix | Delete
}
[425] Fix | Delete
} elseif ( $a[ $this->order_by ] > $b[ $this->order_by ] ) {
[426] Fix | Delete
return strtolower( $this->order ) === 'desc' ? -1 : 1;
[427] Fix | Delete
} elseif ( $a[ $this->order_by ] < $b[ $this->order_by ] ) {
[428] Fix | Delete
return strtolower( $this->order ) === 'desc' ? 1 : -1;
[429] Fix | Delete
}
[430] Fix | Delete
}
[431] Fix | Delete
[432] Fix | Delete
/**
[433] Fix | Delete
* Sorts intervals according to user's request.
[434] Fix | Delete
*
[435] Fix | Delete
* They are pre-sorted in SQL, but after adding gaps, they need to be sorted including the added ones.
[436] Fix | Delete
*
[437] Fix | Delete
* @param stdClass $data Data object, must contain an array under $data->intervals.
[438] Fix | Delete
* @param string $sort_by Ordering property.
[439] Fix | Delete
* @param string $direction DESC/ASC.
[440] Fix | Delete
*/
[441] Fix | Delete
protected function sort_intervals( &$data, $sort_by, $direction ) {
[442] Fix | Delete
$this->sort_array( $data->intervals, $sort_by, $direction );
[443] Fix | Delete
}
[444] Fix | Delete
[445] Fix | Delete
/**
[446] Fix | Delete
* Sorts array of arrays based on subarray key $sort_by.
[447] Fix | Delete
*
[448] Fix | Delete
* @param array $arr Array to sort.
[449] Fix | Delete
* @param string $sort_by Ordering property.
[450] Fix | Delete
* @param string $direction DESC/ASC.
[451] Fix | Delete
*/
[452] Fix | Delete
protected function sort_array( &$arr, $sort_by, $direction ) {
[453] Fix | Delete
$this->order_by = $this->normalize_order_by( $sort_by );
[454] Fix | Delete
$this->order = $direction;
[455] Fix | Delete
usort( $arr, array( $this, 'interval_cmp' ) );
[456] Fix | Delete
}
[457] Fix | Delete
[458] Fix | Delete
/**
[459] Fix | Delete
* Fills in interval gaps from DB with 0-filled objects.
[460] Fix | Delete
*
[461] Fix | Delete
* @param array $db_intervals Array of all intervals present in the db.
[462] Fix | Delete
* @param DateTime $start_datetime Start date.
[463] Fix | Delete
* @param DateTime $end_datetime End date.
[464] Fix | Delete
* @param string $time_interval Time interval, e.g. day, week, month.
[465] Fix | Delete
* @param stdClass $data Data with SQL extracted intervals.
[466] Fix | Delete
* @return stdClass
[467] Fix | Delete
*/
[468] Fix | Delete
protected function fill_in_missing_intervals( $db_intervals, $start_datetime, $end_datetime, $time_interval, &$data ) {
[469] Fix | Delete
// @todo This is ugly and messy.
[470] Fix | Delete
$local_tz = new \DateTimeZone( wc_timezone_string() );
[471] Fix | Delete
// At this point, we don't know when we can stop iterating, as the ordering can be based on any value.
[472] Fix | Delete
$time_ids = array_flip( wp_list_pluck( $data->intervals, 'time_interval' ) );
[473] Fix | Delete
$db_intervals = array_flip( $db_intervals );
[474] Fix | Delete
// Totals object used to get all needed properties.
[475] Fix | Delete
$totals_arr = get_object_vars( $data->totals );
[476] Fix | Delete
foreach ( $totals_arr as $key => $val ) {
[477] Fix | Delete
$totals_arr[ $key ] = 0;
[478] Fix | Delete
}
[479] Fix | Delete
// @todo Should 'products' be in intervals?
[480] Fix | Delete
unset( $totals_arr['products'] );
[481] Fix | Delete
while ( $start_datetime <= $end_datetime ) {
[482] Fix | Delete
$next_start = TimeInterval::iterate( $start_datetime, $time_interval );
[483] Fix | Delete
$time_id = TimeInterval::time_interval_id( $time_interval, $start_datetime );
[484] Fix | Delete
// Either create fill-zero interval or use data from db.
[485] Fix | Delete
if ( $next_start > $end_datetime ) {
[486] Fix | Delete
$interval_end = $end_datetime->format( 'Y-m-d H:i:s' );
[487] Fix | Delete
} else {
[488] Fix | Delete
$prev_end_timestamp = (int) $next_start->format( 'U' ) - 1;
[489] Fix | Delete
$prev_end = new \DateTime();
[490] Fix | Delete
$prev_end->setTimestamp( $prev_end_timestamp );
[491] Fix | Delete
$prev_end->setTimezone( $local_tz );
[492] Fix | Delete
$interval_end = $prev_end->format( 'Y-m-d H:i:s' );
[493] Fix | Delete
}
[494] Fix | Delete
if ( array_key_exists( $time_id, $time_ids ) ) {
[495] Fix | Delete
// For interval present in the db for this time frame, just fill in dates.
[496] Fix | Delete
$record = &$data->intervals[ $time_ids[ $time_id ] ];
[497] Fix | Delete
$record['date_start'] = $start_datetime->format( 'Y-m-d H:i:s' );
[498] Fix | Delete
$record['date_end'] = $interval_end;
[499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function