$post_type, 'post_status' => 'any', 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => 'date', 'order' => 'DESC', ); if ( ! empty( $params['search'] ) ) { $args['s'] = sanitize_text_field( wp_unslash( $params['search'] ) ); } if ( ! empty( $params['status'] ) && 'any' !== $params['status'] ) { $st = sanitize_key( $params['status'] ); if ( 'trash' === $st ) { $args['post_status'] = 'trash'; } else { $args['post_status'] = $st; } } if ( ! empty( $params['author'] ) ) { $args['author'] = absint( $params['author'] ); } if ( ! empty( $params['lang'] ) ) { $args['lang'] = sanitize_text_field( $params['lang'] ); } if ( ! empty( $params['date_from'] ) || ! empty( $params['date_to'] ) ) { $dq = array( 'inclusive' => true ); if ( ! empty( $params['date_from'] ) ) { $dq['after'] = sanitize_text_field( $params['date_from'] ); } if ( ! empty( $params['date_to'] ) ) { $dq['before'] = sanitize_text_field( $params['date_to'] ); } $args['date_query'] = array( $dq ); } $tax_query = array(); $meta_query = array(); // Per-column filters: cf_{key} = "op:value", op in contains,eq,gt,lt,empty. foreach ( (array) $params as $k => $v ) { if ( ! str_starts_with( (string) $k, 'cf_' ) || '' === (string) $v ) { continue; } $ck = substr( (string) $k, 3 ); $parts = explode( ':', (string) $v, 2 ); $op = $parts[0]; $val = $parts[1] ?? ''; $mk = str_starts_with( $ck, 'meta:' ) ? substr( $ck, 5 ) : $ck; if ( 'empty' === $op ) { $meta_query[] = array( 'key' => $mk, 'compare' => 'NOT EXISTS' ); } else { $compare = match ( $op ) { 'eq' => '=', 'gt' => '>', 'lt' => '<', default => 'LIKE' }; $mq = array( 'key' => $mk, 'value' => $val, 'compare' => $compare ); if ( in_array( $op, array( 'gt', 'lt' ), true ) ) { $mq['type'] = 'NUMERIC'; } $meta_query[] = $mq; } } foreach ( (array) $params as $k => $v ) { if ( str_starts_with( $k, 'tax_' ) && '' !== $v ) { $tax = substr( $k, 4 ); if ( taxonomy_exists( $tax ) ) { $tax_query[] = array( 'taxonomy' => $tax, 'field' => 'term_id', 'terms' => array_map( 'intval', (array) $v ), ); } } } if ( count( $tax_query ) > 1 ) { $tax_query['relation'] = 'AND'; } if ( ! empty( $tax_query ) ) { $args['tax_query'] = $tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Taxonomy filter the user chose; results are paged. } if ( ! empty( $params['meta_key'] ) ) { $mq = array( 'key' => sanitize_text_field( $params['meta_key'] ) ); if ( isset( $params['meta_value'] ) && '' !== $params['meta_value'] ) { $mq['value'] = sanitize_text_field( $params['meta_value'] ); $mq['compare'] = 'LIKE'; } else { $mq['compare'] = 'EXISTS'; } $meta_query[] = $mq; } // Woo price range. if ( 'product' === $post_type && ( isset( $params['min_price'] ) || isset( $params['max_price'] ) ) ) { $price_q = array( 'key' => '_price', 'type' => 'DECIMAL(12,4)', ); $min = isset( $params['min_price'] ) && '' !== $params['min_price'] ? (float) $params['min_price'] : null; $max = isset( $params['max_price'] ) && '' !== $params['max_price'] ? (float) $params['max_price'] : null; if ( null !== $min && null !== $max ) { $price_q['compare'] = 'BETWEEN'; $price_q['value'] = array( $min, $max ); } elseif ( null !== $min ) { $price_q['compare'] = '>='; $price_q['value'] = $min; } elseif ( null !== $max ) { $price_q['compare'] = '<='; $price_q['value'] = $max; } $meta_query[] = $price_q; } if ( 'product' === $post_type && ! empty( $params['missing_sku'] ) ) { $meta_query[] = array( 'relation' => 'OR', array( 'key' => '_sku', 'compare' => 'NOT EXISTS' ), array( 'key' => '_sku', 'value' => '', 'compare' => '=' ), ); } if ( 'product' === $post_type && ! empty( $params['missing_image'] ) ) { $meta_query[] = array( 'key' => '_thumbnail_id', 'compare' => 'NOT EXISTS' ); } if ( 'product' === $post_type && ! empty( $params['on_sale'] ) ) { $meta_query[] = array( 'key' => '_sale_price', 'value' => '', 'compare' => '!=' ); } if ( 'product' === $post_type && ! empty( $params['stock_status'] ) ) { $meta_query[] = array( 'key' => '_stock_status', 'value' => sanitize_key( $params['stock_status'] ), 'compare' => '=', ); } if ( count( $meta_query ) > 1 ) { $meta_query['relation'] = 'AND'; } if ( ! empty( $meta_query ) ) { $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Custom field / price / stock filter the user chose; results are paged. } $allowed_orderby = array( 'date', 'modified', 'title', 'ID', 'menu_order', 'comment_count' ); if ( ! empty( $params['orderby'] ) && in_array( $params['orderby'], $allowed_orderby, true ) ) { $args['orderby'] = $params['orderby']; } elseif ( ! empty( $params['orderby'] ) && str_starts_with( $params['orderby'], 'meta:' ) ) { $args['orderby'] = 'meta_value'; $args['meta_key'] = sanitize_text_field( substr( $params['orderby'], 5 ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Sorting by a custom field column the user clicked. } if ( ! empty( $params['order'] ) && in_array( strtoupper( $params['order'] ), array( 'ASC', 'DESC' ), true ) ) { $args['order'] = strtoupper( $params['order'] ); } // Price sorting via meta. if ( ! empty( $params['orderby'] ) && in_array( $params['orderby'], array( '_price', '_regular_price', '_sale_price', '_stock' ), true ) ) { $args['orderby'] = 'meta_value_num'; $args['meta_key'] = sanitize_key( $params['orderby'] ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Sorting by price/stock the user clicked. } // Users who cannot edit other authors' items (authors, or own-items-only mode) see only their own rows. if ( ! Access::can_edit_others( $post_type ) ) { $args['author'] = get_current_user_id(); } $q = new \WP_Query( $args ); $rows = array(); $cols = Columns::get_columns( $post_type ); $wanted = isset( $params['columns'] ) && is_array( $params['columns'] ) && ! empty( $params['columns'] ) ? array_values( array_intersect( array_map( 'sanitize_text_field', $params['columns'] ), array_keys( $cols ) ) ) : array_keys( $cols ); foreach ( $q->posts as $p ) { $row = array( 'ID' => $p->ID ); foreach ( $wanted as $ck ) { $row[ $ck ] = Columns::get_value( $p->ID, $ck ); } $row['_edit_link'] = get_edit_post_link( $p->ID, 'raw' ); $row['_view_link'] = get_permalink( $p->ID ); $rows[] = $row; } return array( 'rows' => $rows, 'total' => (int) $q->found_posts, 'pages' => (int) $q->max_num_pages, ); } }