You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
9.1 KiB
PHP

<?php
/**
* The Analytics Module
*
* @since 1.0.49
* @package RankMath
* @subpackage RankMath\modules
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Analytics;
use WP_REST_Request;
use RankMath\Analytics\Stats;
defined( 'ABSPATH' ) || exit;
/**
* Keywords class.
*/
class Keywords extends Posts {
/**
* Get most recent day's keywords.
*
* @return array
*/
public function get_recent_keywords() {
global $wpdb;
$query = $wpdb->prepare(
"SELECT query
FROM {$wpdb->prefix}rank_math_analytics_gsc
WHERE DATE(created) = (SELECT MAX(DATE(created)) FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE created BETWEEN %s AND %s)
GROUP BY query",
Stats::get()->start_date,
Stats::get()->end_date
);
$data = $wpdb->get_results( $query ); // phpcs:ignore
return $data;
}
/**
* Get keywords data.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_keywords_rows( WP_REST_Request $request ) {
// Get most recent day's keywords only.
$keywords = $this->get_recent_keywords();
$keywords = wp_list_pluck( $keywords, 'query' );
$keywords = array_map( 'esc_sql', $keywords );
$keywords = array_map( 'mb_strtolower', $keywords );
$per_page = 25;
$cache_args = $request->get_params();
$cache_args['per_page'] = $per_page;
$cache_group = 'rank_math_rest_keywords_rows';
$cache_key = $this->generate_hash( $cache_args );
$rows = $this->get_cache( $cache_key, $cache_group );
if ( empty( $rows ) ) {
$rows = $this->get_analytics_data(
[
'dimension' => 'query',
'objects' => false,
'pageview' => false,
'orderBy' => ! empty( $request->get_param( 'orderby' ) ) ? $request->get_param( 'orderby' ) : 'impressions',
'order' => in_array( $request->get_param( 'order' ), [ 'asc', 'desc' ], true ) ? strtoupper( $request->get_param( 'order' ) ) : 'DESC',
'offset' => ( $request->get_param( 'page' ) - 1 ) * $per_page,
'perpage' => $per_page,
'sub_where' => " AND query IN ('" . join( "', '", $keywords ) . "')",
]
);
}
$rows = apply_filters( 'rank_math/analytics/keywords', $rows );
if ( empty( $rows ) ) {
$rows['response'] = 'No Data';
}
return $rows;
}
/**
* Get top keywords overview filtered by keyword position range.
*
* @return object
*/
public function get_top_keywords() {
global $wpdb;
$cache_key = $this->get_cache_key( 'top_keywords', $this->days . 'days' );
$cache = get_transient( $cache_key );
if ( false !== $cache ) {
return $cache;
}
// Get current keywords count filtered by position range.
$query = $wpdb->prepare(
"SELECT COUNT(t1.query) AS total,
CASE
WHEN t1.position BETWEEN 1 AND 3 THEN 'top3'
WHEN t1.position BETWEEN 4 AND 10 THEN 'top10'
WHEN t1.position BETWEEN 11 AND 50 THEN 'top50'
WHEN t1.position BETWEEN 51 AND 100 THEN 'top100'
ELSE 'none'
END AS position_type
FROM (SELECT query, ROUND( AVG(position), 0 ) AS position
FROM {$wpdb->prefix}rank_math_analytics_gsc
WHERE created BETWEEN %s AND %s AND DATE(created) = (SELECT MAX(DATE(created)) FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE created BETWEEN %s AND %s)
GROUP BY query
ORDER BY position) as t1
GROUP BY position_type",
$this->start_date,
$this->end_date,
$this->start_date,
$this->end_date
);
$data = $wpdb->get_results( $query ); // phpcs:ignore
// Get compare keywords count filtered by position range.
$query = $wpdb->prepare(
"SELECT COUNT(t1.query) AS total,
CASE
WHEN t1.position BETWEEN 1 AND 3 THEN 'top3'
WHEN t1.position BETWEEN 4 AND 10 THEN 'top10'
WHEN t1.position BETWEEN 11 AND 50 THEN 'top50'
WHEN t1.position BETWEEN 51 AND 100 THEN 'top100'
ELSE 'none'
END AS position_type
FROM (SELECT query, ROUND( AVG(position), 0 ) AS position
FROM {$wpdb->prefix}rank_math_analytics_gsc
WHERE created BETWEEN %s AND %s AND DATE(created) = (SELECT MAX(DATE(created)) FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE created BETWEEN %s AND %s)
GROUP BY query
ORDER BY position) as t1
GROUP BY position_type",
$this->compare_start_date,
$this->compare_end_date,
$this->compare_start_date,
$this->compare_end_date
);
$compare = $wpdb->get_results( $query ); // phpcs:ignore
$positions = [
'top3' => [
'total' => 0,
'difference' => 0,
],
'top10' => [
'total' => 0,
'difference' => 0,
],
'top50' => [
'total' => 0,
'difference' => 0,
],
'top100' => [
'total' => 0,
'difference' => 0,
],
'ctr' => 0,
'ctrDifference' => 0,
];
// Calculate total and difference for each position range.
$positions = $this->get_top_position_total( $positions, $data, 'total' );
$positions = $this->get_top_position_total( $positions, $compare, 'difference' );
// Get CTR.
$positions['ctr'] = DB::analytics()
->selectAvg( 'ctr', 'ctr' )
->whereBetween( 'created', [ $this->start_date, $this->end_date ] )
->getVar();
// Get compare CTR.
$positions['ctrDifference'] = DB::analytics()
->selectAvg( 'ctr', 'ctr' )
->whereBetween( 'created', [ $this->compare_start_date, $this->compare_end_date ] )
->getVar();
// Calculate current CTR and CTR difference.
$positions['ctr'] = empty( $positions['ctr'] ) ? 0 : $positions['ctr'];
$positions['ctrDifference'] = empty( $positions['ctrDifference'] ) ? 0 : $positions['ctrDifference'];
$positions['ctrDifference'] = $positions['ctr'] - $positions['ctrDifference'];
set_transient( $cache_key, $positions, DAY_IN_SECONDS );
return $positions;
}
/**
* Get position graph
*
* @return array
*/
public function get_top_position_graph() {
global $wpdb;
$cache_key = $this->get_cache_key( 'top_keywords_graph', $this->days . 'days' );
$cache = get_transient( $cache_key );
if ( false !== $cache ) {
return $cache;
}
// Step1. Get splitted date intervals for graph within selected date range.
$intervals = $this->get_intervals();
$sql_daterange = $this->get_sql_date_intervals( $intervals );
// Step2. Get most recent days for each splitted date intervals.
// phpcs:disable
$query = $wpdb->prepare(
"SELECT MAX(DATE(created)) as date, {$sql_daterange}
FROM {$wpdb->prefix}rank_math_analytics_gsc
WHERE created BETWEEN %s AND %s
GROUP BY range_group",
$this->start_date,
$this->end_date
);
$position_dates = $wpdb->get_results( $query, ARRAY_A );
// phpcs:enable
if ( count( $position_dates ) === 0 ) {
return [];
}
$dates = [];
foreach ( $position_dates as $row ) {
array_push( $dates, $row['date'] );
}
$dates = '(\'' . join( '\', \'', $dates ) . '\')';
// Step3. Get keywords count filtered by position range group for each date.
// phpcs:disable
$query = $wpdb->prepare(
"SELECT COUNT(t.query) AS total, t.date,
CASE
WHEN t.position BETWEEN 1 AND 3 THEN 'top3'
WHEN t.position BETWEEN 4 AND 10 THEN 'top10'
WHEN t.position BETWEEN 11 AND 50 THEN 'top50'
WHEN t.position BETWEEN 51 AND 100 THEN 'top100'
ELSE 'none'
END AS position_type
FROM (
SELECT query, ROUND( AVG(position), 0 ) AS position, Date(created) as date
FROM {$wpdb->prefix}rank_math_analytics_gsc
WHERE created BETWEEN %s AND %s AND DATE(created) IN {$dates}
GROUP BY DATE(created), query) AS t
GROUP BY t.date, position_type",
$this->start_date,
$this->end_date
);
$position_data = $wpdb->get_results( $query );
// phpcs:enable
// Construct return data.
$data = $this->get_date_array(
$intervals['dates'],
[
'top3' => 0,
'top10' => 0,
'top50' => 0,
'top100' => 0,
]
);
foreach ( $position_data as $row ) {
if ( ! isset( $intervals['map'][ $row->date ] ) ) {
continue;
}
$date = $intervals['map'][ $row->date ];
if ( ! isset( $data[ $date ][ $row->position_type ] ) ) {
continue;
}
$key = $row->position_type;
$data[ $date ][ $key ] = $row->total;
}
$data = array_values( $data );
set_transient( $cache_key, $data, DAY_IN_SECONDS );
return $data;
}
/**
* Get top position total.
*
* @param array $positions Position array.
* @param array $rows Data to process.
* @param string $where What data to get total.
*
* @return array
*/
private function get_top_position_total( $positions, $rows, $where ) {
foreach ( $rows as $row ) {
$positions[ $row->position_type ][ $where ] = $row->total;
}
if ( 'difference' === $where ) {
$positions['top3']['difference'] = $positions['top3']['total'] - $positions['top3']['difference'];
$positions['top10']['difference'] = $positions['top10']['total'] - $positions['top10']['difference'];
$positions['top50']['difference'] = $positions['top50']['total'] - $positions['top50']['difference'];
$positions['top100']['difference'] = $positions['top100']['total'] - $positions['top100']['difference'];
}
return $positions;
}
}