PageRenderTime 67ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/woocommerce/packages/woocommerce-admin/src/API/Reports/Downloads/DataStore.php

https://gitlab.com/campus-academy/krowkaramel
PHP | 413 lines | 251 code | 53 blank | 109 comment | 26 complexity | 3b870d09987778f6cb7b73605639bca2 MD5 | raw file
  1. <?php
  2. /**
  3. * API\Reports\Downloads\DataStore class file.
  4. */
  5. namespace Automattic\WooCommerce\Admin\API\Reports\Downloads;
  6. defined( 'ABSPATH' ) || exit;
  7. use \Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore;
  8. use \Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface;
  9. use \Automattic\WooCommerce\Admin\API\Reports\TimeInterval;
  10. use \Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
  11. /**
  12. * API\Reports\Downloads\DataStore.
  13. */
  14. class DataStore extends ReportsDataStore implements DataStoreInterface {
  15. /**
  16. * Table used to get the data.
  17. *
  18. * @var string
  19. */
  20. protected static $table_name = 'wc_download_log';
  21. /**
  22. * Cache identifier.
  23. *
  24. * @var string
  25. */
  26. protected $cache_key = 'downloads';
  27. /**
  28. * Mapping columns to data type to return correct response types.
  29. *
  30. * @var array
  31. */
  32. protected $column_types = array(
  33. 'id' => 'intval',
  34. 'date' => 'strval',
  35. 'date_gmt' => 'strval',
  36. 'download_id' => 'strval', // String because this can sometimes be a hash.
  37. 'file_name' => 'strval',
  38. 'product_id' => 'intval',
  39. 'order_id' => 'intval',
  40. 'user_id' => 'intval',
  41. 'ip_address' => 'strval',
  42. );
  43. /**
  44. * Data store context used to pass to filters.
  45. *
  46. * @var string
  47. */
  48. protected $context = 'downloads';
  49. /**
  50. * Assign report columns once full table name has been assigned.
  51. */
  52. protected function assign_report_columns() {
  53. $this->report_columns = array(
  54. 'id' => 'download_log_id as id',
  55. 'date' => 'timestamp as date_gmt',
  56. 'download_id' => 'product_permissions.download_id',
  57. 'product_id' => 'product_permissions.product_id',
  58. 'order_id' => 'product_permissions.order_id',
  59. 'user_id' => 'product_permissions.user_id',
  60. 'ip_address' => 'user_ip_address as ip_address',
  61. );
  62. }
  63. /**
  64. * Updates the database query with parameters used for downloads report.
  65. *
  66. * @param array $query_args Query arguments supplied by the user.
  67. */
  68. protected function add_sql_query_params( $query_args ) {
  69. global $wpdb;
  70. $lookup_table = self::get_db_table_name();
  71. $permission_table = $wpdb->prefix . 'woocommerce_downloadable_product_permissions';
  72. $operator = $this->get_match_operator( $query_args );
  73. $where_filters = array();
  74. $join = "JOIN {$permission_table} as product_permissions ON {$lookup_table}.permission_id = product_permissions.permission_id";
  75. $where_time = $this->add_time_period_sql_params( $query_args, $lookup_table );
  76. if ( $where_time ) {
  77. if ( isset( $this->subquery ) ) {
  78. $this->subquery->add_sql_clause( 'where_time', $where_time );
  79. } else {
  80. $this->interval_query->add_sql_clause( 'where_time', $where_time );
  81. }
  82. }
  83. $this->get_limit_sql_params( $query_args );
  84. $where_filters[] = $this->get_object_where_filter(
  85. $lookup_table,
  86. 'permission_id',
  87. $permission_table,
  88. 'product_id',
  89. 'IN',
  90. $this->get_included_products( $query_args )
  91. );
  92. $where_filters[] = $this->get_object_where_filter(
  93. $lookup_table,
  94. 'permission_id',
  95. $permission_table,
  96. 'product_id',
  97. 'NOT IN',
  98. $this->get_excluded_products( $query_args )
  99. );
  100. $where_filters[] = $this->get_object_where_filter(
  101. $lookup_table,
  102. 'permission_id',
  103. $permission_table,
  104. 'order_id',
  105. 'IN',
  106. $this->get_included_orders( $query_args )
  107. );
  108. $where_filters[] = $this->get_object_where_filter(
  109. $lookup_table,
  110. 'permission_id',
  111. $permission_table,
  112. 'order_id',
  113. 'NOT IN',
  114. $this->get_excluded_orders( $query_args )
  115. );
  116. $customer_lookup_table = $wpdb->prefix . 'wc_customer_lookup';
  117. $customer_lookup = "SELECT {$customer_lookup_table}.user_id FROM {$customer_lookup_table} WHERE {$customer_lookup_table}.customer_id IN (%s)";
  118. $included_customers = $this->get_included_customers( $query_args );
  119. $excluded_customers = $this->get_excluded_customers( $query_args );
  120. if ( $included_customers ) {
  121. $where_filters[] = $this->get_object_where_filter(
  122. $lookup_table,
  123. 'permission_id',
  124. $permission_table,
  125. 'user_id',
  126. 'IN',
  127. sprintf( $customer_lookup, $included_customers )
  128. );
  129. }
  130. if ( $excluded_customers ) {
  131. $where_filters[] = $this->get_object_where_filter(
  132. $lookup_table,
  133. 'permission_id',
  134. $permission_table,
  135. 'user_id',
  136. 'NOT IN',
  137. sprintf( $customer_lookup, $excluded_customers )
  138. );
  139. }
  140. $included_ip_addresses = $this->get_included_ip_addresses( $query_args );
  141. $excluded_ip_addresses = $this->get_excluded_ip_addresses( $query_args );
  142. if ( $included_ip_addresses ) {
  143. $where_filters[] = "{$lookup_table}.user_ip_address IN ('{$included_ip_addresses}')";
  144. }
  145. if ( $excluded_ip_addresses ) {
  146. $where_filters[] = "{$lookup_table}.user_ip_address NOT IN ('{$excluded_ip_addresses}')";
  147. }
  148. $where_filters = array_filter( $where_filters );
  149. $where_subclause = implode( " $operator ", $where_filters );
  150. if ( $where_subclause ) {
  151. if ( isset( $this->subquery ) ) {
  152. $this->subquery->add_sql_clause( 'where', "AND ( $where_subclause )" );
  153. } else {
  154. $this->interval_query->add_sql_clause( 'where', "AND ( $where_subclause )" );
  155. }
  156. }
  157. if ( isset( $this->subquery ) ) {
  158. $this->subquery->add_sql_clause( 'join', $join );
  159. } else {
  160. $this->interval_query->add_sql_clause( 'join', $join );
  161. }
  162. $this->add_order_by( $query_args );
  163. }
  164. /**
  165. * Returns comma separated ids of included ip address, based on query arguments from the user.
  166. *
  167. * @param array $query_args Parameters supplied by the user.
  168. * @return string
  169. */
  170. protected function get_included_ip_addresses( $query_args ) {
  171. return $this->get_filtered_ip_addresses( $query_args, 'ip_address_includes' );
  172. }
  173. /**
  174. * Returns comma separated ids of excluded ip address, based on query arguments from the user.
  175. *
  176. * @param array $query_args Parameters supplied by the user.
  177. * @return string
  178. */
  179. protected function get_excluded_ip_addresses( $query_args ) {
  180. return $this->get_filtered_ip_addresses( $query_args, 'ip_address_excludes' );
  181. }
  182. /**
  183. * Returns filtered comma separated ids, based on query arguments from the user.
  184. *
  185. * @param array $query_args Parameters supplied by the user.
  186. * @param string $field Query field to filter.
  187. * @return string
  188. */
  189. protected function get_filtered_ip_addresses( $query_args, $field ) {
  190. if ( isset( $query_args[ $field ] ) && is_array( $query_args[ $field ] ) && count( $query_args[ $field ] ) > 0 ) {
  191. $ip_addresses = array_map( 'esc_sql', $query_args[ $field ] );
  192. /**
  193. * Filter the IDs before retrieving report data.
  194. *
  195. * Allows filtering of the objects included or excluded from reports.
  196. *
  197. * @param array $ids List of object Ids.
  198. * @param array $query_args The original arguments for the request.
  199. * @param string $field The object type.
  200. * @param string $context The data store context.
  201. */
  202. $ip_addresses = apply_filters( 'woocommerce_analytics_' . $field, $ip_addresses, $query_args, $field, $this->context );
  203. return implode( "','", $ip_addresses );
  204. }
  205. return '';
  206. }
  207. /**
  208. * Returns comma separated ids of included customers, based on query arguments from the user.
  209. *
  210. * @param array $query_args Parameters supplied by the user.
  211. * @return string
  212. */
  213. protected function get_included_customers( $query_args ) {
  214. return self::get_filtered_ids( $query_args, 'customer_includes' );
  215. }
  216. /**
  217. * Returns comma separated ids of excluded customers, based on query arguments from the user.
  218. *
  219. * @param array $query_args Parameters supplied by the user.
  220. * @return string
  221. */
  222. protected function get_excluded_customers( $query_args ) {
  223. return self::get_filtered_ids( $query_args, 'customer_excludes' );
  224. }
  225. /**
  226. * Gets WHERE time clause of SQL request with date-related constraints.
  227. *
  228. * @param array $query_args Parameters supplied by the user.
  229. * @param string $table_name Name of the db table relevant for the date constraint.
  230. * @return string
  231. */
  232. protected function add_time_period_sql_params( $query_args, $table_name ) {
  233. $where_time = '';
  234. if ( $query_args['before'] ) {
  235. $datetime_str = $query_args['before']->format( TimeInterval::$sql_datetime_format );
  236. $where_time .= " AND {$table_name}.timestamp <= '$datetime_str'";
  237. }
  238. if ( $query_args['after'] ) {
  239. $datetime_str = $query_args['after']->format( TimeInterval::$sql_datetime_format );
  240. $where_time .= " AND {$table_name}.timestamp >= '$datetime_str'";
  241. }
  242. return $where_time;
  243. }
  244. /**
  245. * Fills ORDER BY clause of SQL request based on user supplied parameters.
  246. *
  247. * @param array $query_args Parameters supplied by the user.
  248. */
  249. protected function add_order_by( $query_args ) {
  250. global $wpdb;
  251. $this->clear_sql_clause( 'order_by' );
  252. $order_by = '';
  253. if ( isset( $query_args['orderby'] ) ) {
  254. $order_by = $this->normalize_order_by( esc_sql( $query_args['orderby'] ) );
  255. $this->add_sql_clause( 'order_by', $order_by );
  256. }
  257. if ( false !== strpos( $order_by, '_products' ) ) {
  258. $this->subquery->add_sql_clause( 'join', "JOIN {$wpdb->posts} AS _products ON product_permissions.product_id = _products.ID" );
  259. }
  260. $this->add_orderby_order_clause( $query_args, $this );
  261. }
  262. /**
  263. * Returns the report data based on parameters supplied by the user.
  264. *
  265. * @param array $query_args Query parameters.
  266. * @return stdClass|WP_Error Data.
  267. */
  268. public function get_data( $query_args ) {
  269. global $wpdb;
  270. $table_name = self::get_db_table_name();
  271. // These defaults are only partially applied when used via REST API, as that has its own defaults.
  272. $defaults = array(
  273. 'per_page' => get_option( 'posts_per_page' ),
  274. 'page' => 1,
  275. 'order' => 'DESC',
  276. 'orderby' => 'timestamp',
  277. 'before' => TimeInterval::default_before(),
  278. 'after' => TimeInterval::default_after(),
  279. 'fields' => '*',
  280. );
  281. $query_args = wp_parse_args( $query_args, $defaults );
  282. $this->normalize_timezones( $query_args, $defaults );
  283. /*
  284. * We need to get the cache key here because
  285. * parent::update_intervals_sql_params() modifies $query_args.
  286. */
  287. $cache_key = $this->get_cache_key( $query_args );
  288. $data = $this->get_cached_data( $cache_key );
  289. if ( false === $data ) {
  290. $this->initialize_queries();
  291. $data = (object) array(
  292. 'data' => array(),
  293. 'total' => 0,
  294. 'pages' => 0,
  295. 'page_no' => 0,
  296. );
  297. $selections = $this->selected_columns( $query_args );
  298. $this->add_sql_query_params( $query_args );
  299. // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
  300. $db_records_count = (int) $wpdb->get_var(
  301. "SELECT COUNT(*) FROM (
  302. {$this->subquery->get_query_statement()}
  303. ) AS tt"
  304. );
  305. // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
  306. $params = $this->get_limit_params( $query_args );
  307. $total_pages = (int) ceil( $db_records_count / $params['per_page'] );
  308. if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) {
  309. return $data;
  310. }
  311. $this->subquery->clear_sql_clause( 'select' );
  312. $this->subquery->add_sql_clause( 'select', $selections );
  313. $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) );
  314. $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) );
  315. $download_data = $wpdb->get_results(
  316. $this->subquery->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  317. ARRAY_A
  318. );
  319. if ( null === $download_data ) {
  320. return $data;
  321. }
  322. $download_data = array_map( array( $this, 'cast_numbers' ), $download_data );
  323. $data = (object) array(
  324. 'data' => $download_data,
  325. 'total' => $db_records_count,
  326. 'pages' => $total_pages,
  327. 'page_no' => (int) $query_args['page'],
  328. );
  329. $this->set_cached_data( $cache_key, $data );
  330. }
  331. return $data;
  332. }
  333. /**
  334. * Maps ordering specified by the user to columns in the database/fields in the data.
  335. *
  336. * @param string $order_by Sorting criterion.
  337. * @return string
  338. */
  339. protected function normalize_order_by( $order_by ) {
  340. global $wpdb;
  341. if ( 'date' === $order_by ) {
  342. return $wpdb->prefix . 'wc_download_log.timestamp';
  343. }
  344. if ( 'product' === $order_by ) {
  345. return '_products.post_title';
  346. }
  347. return $order_by;
  348. }
  349. /**
  350. * Initialize query objects.
  351. */
  352. protected function initialize_queries() {
  353. $this->clear_all_clauses();
  354. $table_name = self::get_db_table_name();
  355. $this->subquery = new SqlQuery( $this->context . '_subquery' );
  356. $this->subquery->add_sql_clause( 'from', $table_name );
  357. $this->subquery->add_sql_clause( 'select', "{$table_name}.download_log_id" );
  358. $this->subquery->add_sql_clause( 'group_by', "{$table_name}.download_log_id" );
  359. }
  360. }