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
PHTML
317 lines
9.1 KiB
PHTML
8 months ago
|
<?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;
|
||
|
}
|
||
|
}
|