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.
753 lines
23 KiB
PHTML
753 lines
23 KiB
PHTML
8 months ago
|
<?php
|
||
|
/**
|
||
|
* The Analytics Module
|
||
|
*
|
||
|
* @since 2.0.0
|
||
|
* @package RankMathPro
|
||
|
* @subpackage RankMathPro\modules
|
||
|
* @author Rank Math <support@rankmath.com>
|
||
|
*/
|
||
|
|
||
|
namespace RankMathPro\Analytics;
|
||
|
|
||
|
use WP_REST_Request;
|
||
|
use RankMath\Traits\Cache;
|
||
|
use RankMath\Traits\Hooker;
|
||
|
use RankMath\Analytics\Stats;
|
||
|
use RankMath\Helper;
|
||
|
use MyThemeShop\Helpers\Param;
|
||
|
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* Keywords class.
|
||
|
*/
|
||
|
class Keywords {
|
||
|
|
||
|
use Hooker, Cache;
|
||
|
|
||
|
/**
|
||
|
* Main instance
|
||
|
*
|
||
|
* Ensure only one instance is loaded or can be loaded.
|
||
|
*
|
||
|
* @return Keywords
|
||
|
*/
|
||
|
public static function get() {
|
||
|
static $instance;
|
||
|
|
||
|
if ( is_null( $instance ) && ! ( $instance instanceof Keywords ) ) {
|
||
|
$instance = new Keywords();
|
||
|
$instance->setup();
|
||
|
}
|
||
|
|
||
|
return $instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize filter.
|
||
|
*/
|
||
|
public function setup() {
|
||
|
$this->filter( 'rank_math/analytics/keywords', 'add_keyword_position_graph' );
|
||
|
$this->filter( 'rank_math/analytics/keywords_overview', 'add_winning_losing_data' );
|
||
|
$this->action( 'save_post', 'add_post_focus_keyword' );
|
||
|
$this->action( 'init', 'get_post_type_list', 99 );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get accessible post type lists to auto add the focus keywords.
|
||
|
*/
|
||
|
public function get_post_type_list() {
|
||
|
if ( 'rank-math-analytics' !== Param::get( 'page' ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$post_types = array_map(
|
||
|
function( $post_type ) {
|
||
|
return 'attachment' === $post_type ? false : Helper::get_post_type_label( $post_type );
|
||
|
},
|
||
|
Helper::get_accessible_post_types()
|
||
|
);
|
||
|
Helper::add_json( 'postTypes', array_filter( $post_types ) );
|
||
|
Helper::add_json( 'autoAddFK', Helper::get_settings( 'general.auto_add_focus_keywords', [] ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get keywords position data to show it in the graph.
|
||
|
*
|
||
|
* @param array $rows Rows.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function add_keyword_position_graph( $rows ) {
|
||
|
$history = $this->get_graph_data_for_keywords( \array_keys( $rows ) );
|
||
|
$rows = Stats::get()->set_query_position( $rows, $history );
|
||
|
|
||
|
return $rows;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get winning and losing keywords data.
|
||
|
*
|
||
|
* @param array $data Data.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function add_winning_losing_data( $data ) {
|
||
|
$data['winningKeywords'] = $this->get_winning_keywords();
|
||
|
$data['losingKeywords'] = $this->get_losing_keywords();
|
||
|
|
||
|
if ( empty( $data['winningKeywords'] ) ) {
|
||
|
$data['winningKeywords']['response'] = 'No Data';
|
||
|
}
|
||
|
if ( empty( $data['losingKeywords'] ) ) {
|
||
|
$data['losingKeywords']['response'] = 'No Data';
|
||
|
}
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extract keywords that can be added by removing the empty and the duplicate keywords.
|
||
|
*
|
||
|
* @param string $keywords Comma Separated Keyword List.
|
||
|
*
|
||
|
* @return array Keywords that can be added.
|
||
|
*/
|
||
|
public function extract_addable_track_keyword( $keywords ) {
|
||
|
global $wpdb;
|
||
|
|
||
|
// Split keywords.
|
||
|
$keywords_to_add = array_filter( array_map( 'trim', explode( ',', $keywords ) ) );
|
||
|
$keywords_to_check = array_filter( array_map( 'mb_strtolower', explode( ',', $keywords ) ) );
|
||
|
|
||
|
// Check if keywords already exists.
|
||
|
$keywords_joined = "'" . join( "', '", array_map( 'esc_sql', $keywords_to_add ) ) . "'";
|
||
|
$query = "SELECT keyword FROM {$wpdb->prefix}rank_math_analytics_keyword_manager as km WHERE km.keyword IN ( $keywords_joined )";
|
||
|
$data = $wpdb->get_results( $query ); // phpcs:ignore
|
||
|
|
||
|
// Filter out non-existing keywords.
|
||
|
foreach ( $data as $row ) {
|
||
|
$key = array_search( mb_strtolower( $row->keyword ), $keywords_to_check, true );
|
||
|
if ( false !== $key ) {
|
||
|
unset( $keywords_to_add[ $key ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $keywords_to_add;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add keyword to Rank Tracker.
|
||
|
*
|
||
|
* @param array $keywords Keyword List.
|
||
|
*/
|
||
|
public function add_track_keyword( $keywords ) {
|
||
|
foreach ( $keywords as $add_keyword ) {
|
||
|
DB::keywords()->insert(
|
||
|
[
|
||
|
'keyword' => $add_keyword,
|
||
|
'collection' => 'uncategorized',
|
||
|
'is_active' => true,
|
||
|
],
|
||
|
[ '%s', '%s', '%d' ]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
delete_transient( Stats::get()->get_cache_key( 'tracked_keywords_summary', Stats::get()->days . 'days' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove a keyword from Rank Tracker.
|
||
|
*
|
||
|
* @param string $keyword Keyword to remove.
|
||
|
*/
|
||
|
public function remove_track_keyword( $keyword ) {
|
||
|
DB::keywords()->where( 'keyword', $keyword )
|
||
|
->delete();
|
||
|
|
||
|
delete_transient( Stats::get()->get_cache_key( 'tracked_keywords_summary', Stats::get()->days . 'days' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete all tracked keywords.
|
||
|
*/
|
||
|
public function delete_all_tracked_keywords() {
|
||
|
DB::keywords()->delete();
|
||
|
delete_transient( Stats::get()->get_cache_key( 'tracked_keywords_summary', Stats::get()->days . 'days' ) );
|
||
|
}
|
||
|
/**
|
||
|
* Get tracked keywords count.
|
||
|
*
|
||
|
* @return int Total keywords count
|
||
|
*/
|
||
|
public function get_tracked_keywords_count() {
|
||
|
$total = DB::keywords()
|
||
|
->selectCount( 'DISTINCT(keyword)', 'total' )
|
||
|
->where( 'is_active', 1 )
|
||
|
->getVar();
|
||
|
|
||
|
return (int) $total;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get keywords quota.
|
||
|
*
|
||
|
* @return array Keywords usage info.
|
||
|
*/
|
||
|
public function get_tracked_keywords_quota() {
|
||
|
$quota = (array) get_option(
|
||
|
'rank_math_keyword_quota',
|
||
|
[
|
||
|
'taken' => 0,
|
||
|
'available' => 0,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
return $quota;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get tracked keywords summary.
|
||
|
*
|
||
|
* @return array Keywords usage info.
|
||
|
*/
|
||
|
public function get_tracked_keywords_summary() {
|
||
|
$cache_key = 'tracked_keywords_summary';
|
||
|
$cache_group = 'tracked_keywords_summary';
|
||
|
$summary = $this->get_cache( $cache_key, $cache_group );
|
||
|
|
||
|
if ( empty( $summary ) ) {
|
||
|
$summary = $this->get_tracked_keywords_quota();
|
||
|
$summary['total'] = $this->get_tracked_keywords_count();
|
||
|
$this->set_cache( $cache_key, $summary, $cache_group, DAY_IN_SECONDS );
|
||
|
}
|
||
|
|
||
|
return $summary;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get winning tracked keywords.
|
||
|
*
|
||
|
* @return array Top 5 winning tracked keywords data.
|
||
|
*/
|
||
|
public function get_tracked_winning_keywords() {
|
||
|
return $this->get_tracked_keywords(
|
||
|
[
|
||
|
'offset' => 0,
|
||
|
'perpage' => 5,
|
||
|
'where' => 'WHERE COALESCE( ROUND( t1.position - COALESCE( t2.position, 100 ), 0 ), 0 ) < 0',
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get losing tracked keywords.
|
||
|
*
|
||
|
* @return array Top 5 losing tracked keywords data.
|
||
|
*/
|
||
|
public function get_tracked_losing_keywords() {
|
||
|
return $this->get_tracked_keywords(
|
||
|
[
|
||
|
'order' => 'DESC',
|
||
|
'offset' => 0,
|
||
|
'perpage' => 5,
|
||
|
'where' => 'WHERE COALESCE( ROUND( t1.position - COALESCE( t2.position, 100 ), 0 ), 0 ) > 0',
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get tracked keywords rows.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return array Tracked keywords data.
|
||
|
*/
|
||
|
public function get_tracked_keywords_rows( WP_REST_Request $request ) {
|
||
|
$per_page = 25;
|
||
|
|
||
|
$cache_args = $request->get_params();
|
||
|
$cache_args['per_page'] = $per_page;
|
||
|
|
||
|
$cache_group = 'rank_math_rest_tracked_keywords_rows';
|
||
|
$cache_key = $this->generate_hash( $cache_args );
|
||
|
$result = $this->get_cache( $cache_key, $cache_group );
|
||
|
if ( ! empty( $result ) ) {
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
$page = ! empty( $request->get_param( 'page' ) ) ? $request->get_param( 'page' ) : 1;
|
||
|
$orderby = ! empty( $request->get_param( 'orderby' ) ) ? $request->get_param( 'orderby' ) : 'default';
|
||
|
$order = ! empty( $request->get_param( 'order' ) ) ? strtoupper( $request->get_param( 'order' ) ) : 'DESC';
|
||
|
$keyword = ! empty( $request->get_param( 'search' ) ) ? filter_var( urldecode( $request->get_param( 'search' ) ), FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_BACKTICK ) : '';
|
||
|
$offset = ( $page - 1 ) * $per_page;
|
||
|
$args = wp_parse_args(
|
||
|
[
|
||
|
|
||
|
'dimension' => 'query',
|
||
|
'limit' => "LIMIT {$offset}, {$per_page}",
|
||
|
'keyword' => $keyword,
|
||
|
]
|
||
|
);
|
||
|
switch ( $orderby ) {
|
||
|
case 'impressions':
|
||
|
case 'clicks':
|
||
|
case 'ctr':
|
||
|
case 'position':
|
||
|
$args['orderBy'] = $orderby;
|
||
|
$args['order'] = $order;
|
||
|
break;
|
||
|
case 'query':
|
||
|
$args['orderBy'] = 'keyword';
|
||
|
$args['order'] = $order;
|
||
|
break;
|
||
|
}
|
||
|
$data = $this->get_tracked_keywords_data( $args );
|
||
|
$data = Stats::get()->set_dimension_as_key( $data );
|
||
|
$history = $this->get_graph_data_for_keywords( \array_keys( $data ) );
|
||
|
$data = Stats::get()->set_query_position( $data, $history );
|
||
|
|
||
|
if ( 'default' === $orderby ) {
|
||
|
uasort(
|
||
|
$data,
|
||
|
function( $a, $b ) use ( $orderby ) {
|
||
|
if ( false === array_key_exists( 'position', $a ) ) {
|
||
|
$a['position'] = [ 'total' => '0' ];
|
||
|
}
|
||
|
if ( false === array_key_exists( 'position', $b ) ) {
|
||
|
$b['position'] = [ 'total' => '0' ];
|
||
|
}
|
||
|
|
||
|
if ( 0 === intval( $b['position']['total'] ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return $a['position']['total'] > $b['position']['total'];
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$result['rowsData'] = $data;
|
||
|
// get total rows by search.
|
||
|
$args = wp_parse_args(
|
||
|
[
|
||
|
|
||
|
'dimension' => 'query',
|
||
|
'limit' => 'LIMIT 10000',
|
||
|
'keyword' => $keyword,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
if ( empty( $data ) ) {
|
||
|
$result['response'] = 'No Data';
|
||
|
} else {
|
||
|
$search_data = $this->get_tracked_keywords_data( $args );
|
||
|
$result['total'] = count( $search_data );
|
||
|
|
||
|
$this->set_cache( $cache_key, $result, $cache_group, DAY_IN_SECONDS );
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get keyword rows from keyword manager table.
|
||
|
*
|
||
|
* @param array $args Array of arguments.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_tracked_keywords_data( $args = [] ) {
|
||
|
global $wpdb;
|
||
|
Helper::enable_big_selects_for_queries();
|
||
|
$args = wp_parse_args(
|
||
|
$args,
|
||
|
[
|
||
|
'dimension' => 'query',
|
||
|
'order' => 'ASC',
|
||
|
'orderBy' => 'diffPosition1',
|
||
|
'objects' => false,
|
||
|
'where' => '',
|
||
|
'sub_where' => '',
|
||
|
'dates' => ' AND created BETWEEN %s AND %s',
|
||
|
'limit' => 'LIMIT 5',
|
||
|
'keyword' => '',
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$where = $args['where'];
|
||
|
$limit = $args['limit'];
|
||
|
$dimension = $args['dimension'];
|
||
|
$sub_where = $args['sub_where'];
|
||
|
$dates = $args['dates'];
|
||
|
$keyword = trim( $args['keyword'] );
|
||
|
$order = sprintf( 'ORDER BY %s %s', $args['orderBy'], $args['order'] );
|
||
|
$dates_query = sprintf( " AND created BETWEEN '%s' AND '%s' ", Stats::get()->start_date, Stats::get()->end_date );
|
||
|
// Step1. Get most recent data row id for each keyword.
|
||
|
// phpcs:disable
|
||
|
$where_like_keyword = $wpdb->prepare( ' WHERE keyword LIKE %s', '%' . $wpdb->esc_like( $keyword ) . '%' );
|
||
|
if ( empty( $keyword ) ) {
|
||
|
$where_like_keyword = '';
|
||
|
}
|
||
|
|
||
|
$query = "SELECT MAX(id) as id FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE 1 = 1 {$dates_query} AND {$dimension} IN ( SELECT keyword from {$wpdb->prefix}rank_math_analytics_keyword_manager {$where_like_keyword} GROUP BY keyword ) GROUP BY {$dimension}";
|
||
|
$ids = $wpdb->get_results( $query );
|
||
|
// phpcs:enable
|
||
|
// Step2. Get id list from above result.
|
||
|
$ids = wp_list_pluck( $ids, 'id' );
|
||
|
$ids_where = " AND id IN ('" . join( "', '", $ids ) . "')";
|
||
|
|
||
|
// Step3. Get most recent data row id for each keyword (for comparison).
|
||
|
// phpcs:disable
|
||
|
$dates_query = sprintf( " AND created BETWEEN '%s' AND '%s' ", Stats::get()->compare_start_date, Stats::get()->compare_end_date );
|
||
|
$query = "SELECT MAX(id) as id FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE 1 = 1 {$dates_query} AND {$dimension} IN ( SELECT keyword from {$wpdb->prefix}rank_math_analytics_keyword_manager {$where_like_keyword} GROUP BY keyword ) GROUP BY {$dimension}";
|
||
|
$old_ids = $wpdb->get_results( $query );
|
||
|
|
||
|
// Step4. Get id list from above result.
|
||
|
$old_ids = wp_list_pluck( $old_ids, 'id' );
|
||
|
$old_ids_where = " AND id IN ('" . join( "', '", $old_ids ) . "')";
|
||
|
|
||
|
// Step5. Get most performing keywords first based on id list from above.
|
||
|
$where_like_keyword1 = $wpdb->prepare( ' WHERE km.keyword LIKE %s', '%' . $wpdb->esc_like( $keyword ) . '%' );
|
||
|
if ( empty( $keyword ) ) {
|
||
|
$where_like_keyword1 = '';
|
||
|
}
|
||
|
|
||
|
$positions = $wpdb->get_results(
|
||
|
"SELECT DISTINCT(km.keyword) as {$dimension}, COALESCE(t.position, 0) as position, COALESCE(t.diffPosition, 0) as diffPosition, COALESCE(t.diffPosition, 100) as diffPosition1, COALESCE(t.impressions, 0) as impressions, COALESCE(t.diffImpressions, 0) as diffImpressions, COALESCE(t.clicks, 0) as clicks, COALESCE(t.diffClicks, 0) as diffClicks, COALESCE(t.ctr, 0) as ctr, COALESCE(t.diffCtr, 0) as diffCtr
|
||
|
FROM {$wpdb->prefix}rank_math_analytics_keyword_manager km
|
||
|
LEFT JOIN (
|
||
|
SELECT
|
||
|
t1.{$dimension} as {$dimension}, ROUND( t1.position, 0 ) as position, ROUND( t1.impressions, 0 ) as impressions, ROUND( t1.clicks, 0 ) as clicks, ROUND( t1.ctr, 0 ) as ctr,
|
||
|
COALESCE( ROUND( t1.position - COALESCE( t2.position, 100 ), 0 ), 0 ) as diffPosition,
|
||
|
COALESCE( ROUND( t1.impressions - COALESCE( t2.impressions, 100 ), 0 ), 0 ) as diffImpressions,
|
||
|
COALESCE( ROUND( t1.clicks - COALESCE( t2.clicks, 100 ), 0 ), 0 ) as diffClicks,
|
||
|
COALESCE( ROUND( t1.ctr - COALESCE( t2.ctr, 100 ), 0 ), 0 ) as diffCtr
|
||
|
FROM
|
||
|
(SELECT a.{$dimension}, a.position, a.impressions,a.clicks,a.ctr FROM {$wpdb->prefix}rank_math_analytics_gsc AS a
|
||
|
WHERE 1 = 1{$ids_where}) AS t1
|
||
|
LEFT JOIN
|
||
|
(SELECT a.{$dimension}, a.position, a.impressions,a.clicks,a.ctr FROM {$wpdb->prefix}rank_math_analytics_gsc AS a
|
||
|
WHERE 1 = 1{$old_ids_where}) AS t2
|
||
|
ON t1.{$dimension} = t2.{$dimension}) AS t on t.{$dimension} = km.keyword
|
||
|
{$where_like_keyword1}
|
||
|
{$where}
|
||
|
{$order}
|
||
|
{$limit}",
|
||
|
ARRAY_A
|
||
|
);
|
||
|
// phpcs:enable
|
||
|
|
||
|
// Step6. Get keywords list from above results.
|
||
|
$keywords = array_column( $positions, 'query' );
|
||
|
$keywords = array_map( 'esc_sql', $keywords );
|
||
|
$keywords = array_map( 'strtolower', $keywords );
|
||
|
$keywords = '(\'' . join( '\', \'', $keywords ) . '\')';
|
||
|
|
||
|
// step7. Get other metrics data.
|
||
|
$query = $wpdb->prepare(
|
||
|
"SELECT t1.{$dimension} as {$dimension}, t1.clicks, t1.impressions, t1.ctr,
|
||
|
COALESCE( t1.clicks - t2.clicks, 0 ) as diffClicks,
|
||
|
COALESCE( t1.impressions - t2.impressions, 0 ) as diffImpressions,
|
||
|
COALESCE( t1.ctr - t2.ctr, 0 ) as diffCtr
|
||
|
FROM
|
||
|
( SELECT {$dimension}, SUM( clicks ) as clicks, SUM(impressions) as impressions, AVG(position) as position, AVG(ctr) as ctr FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE 1 = 1{$dates} AND {$dimension} IN {$keywords} GROUP BY {$dimension}) as t1
|
||
|
LEFT JOIN
|
||
|
( SELECT {$dimension}, SUM( clicks ) as clicks, SUM(impressions) as impressions, AVG(position) as position, AVG(ctr) as ctr FROM {$wpdb->prefix}rank_math_analytics_gsc WHERE 1 = 1{$dates} AND {$dimension} IN {$keywords} GROUP BY {$dimension}) as t2
|
||
|
ON t1.query = t2.query",
|
||
|
Stats::get()->start_date,
|
||
|
Stats::get()->end_date,
|
||
|
Stats::get()->compare_start_date,
|
||
|
Stats::get()->compare_end_date
|
||
|
);
|
||
|
$metrics = $wpdb->get_results( $query, ARRAY_A );
|
||
|
|
||
|
// Step8. Merge above two results.
|
||
|
$positions = Stats::get()->set_dimension_as_key( $positions, $dimension );
|
||
|
$metrics = Stats::get()->set_dimension_as_key( $metrics, $dimension );
|
||
|
$data = Stats::get()->get_merged_metrics( $positions, $metrics );
|
||
|
|
||
|
// Step9. Construct return data.
|
||
|
foreach ( $data as $keyword => $row ) {
|
||
|
$data[ $keyword ]['graph'] = [];
|
||
|
|
||
|
$data[ $keyword ]['clicks'] = [
|
||
|
'total' => (int) $data[ $keyword ]['clicks'],
|
||
|
'difference' => (int) $data[ $keyword ]['diffClicks'],
|
||
|
];
|
||
|
|
||
|
$data[ $keyword ]['impressions'] = [
|
||
|
'total' => (int) $data[ $keyword ]['impressions'],
|
||
|
'difference' => (int) $data[ $keyword ]['diffImpressions'],
|
||
|
];
|
||
|
|
||
|
$data[ $keyword ]['position'] = [
|
||
|
'total' => (float) $data[ $keyword ]['position'],
|
||
|
'difference' => (float) $data[ $keyword ]['diffPosition'],
|
||
|
];
|
||
|
|
||
|
$data[ $keyword ]['ctr'] = [
|
||
|
'total' => (float) $data[ $keyword ]['ctr'],
|
||
|
'difference' => (float) $data[ $keyword ]['diffCtr'],
|
||
|
];
|
||
|
|
||
|
unset(
|
||
|
$data[ $keyword ]['diffClicks'],
|
||
|
$data[ $keyword ]['diffImpressions'],
|
||
|
$data[ $keyword ]['diffPosition'],
|
||
|
$data[ $keyword ]['diffCtr']
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get tracked keywords.
|
||
|
*
|
||
|
* @param array $args Array of arguments.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_tracked_keywords( $args = [] ) {
|
||
|
global $wpdb;
|
||
|
|
||
|
$args = wp_parse_args(
|
||
|
$args,
|
||
|
[
|
||
|
'dimension' => 'query',
|
||
|
'order' => 'ASC',
|
||
|
'orderBy' => 'diffPosition',
|
||
|
'offset' => 0,
|
||
|
'perpage' => 20000,
|
||
|
'sub_where' => " AND query IN ( SELECT keyword from {$wpdb->prefix}rank_math_analytics_keyword_manager )",
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$data = Stats::get()->get_analytics_data( $args );
|
||
|
$history = $this->get_graph_data_for_keywords( \array_keys( $data ) );
|
||
|
$data = Stats::get()->set_query_position( $data, $history );
|
||
|
|
||
|
// Add remaining keywords.
|
||
|
if ( 5 !== $args['perpage'] ) {
|
||
|
$rows = DB::keywords()->get();
|
||
|
foreach ( $rows as $row ) {
|
||
|
if ( ! isset( $data[ $row->keyword ] ) ) {
|
||
|
$data[ $row->keyword ] = [
|
||
|
'query' => $row->keyword,
|
||
|
'graph' => [],
|
||
|
'clicks' => [
|
||
|
'total' => 0,
|
||
|
'difference' => 0,
|
||
|
],
|
||
|
'impressions' => [
|
||
|
'total' => 0,
|
||
|
'difference' => 0,
|
||
|
],
|
||
|
'position' => [
|
||
|
'total' => 0,
|
||
|
'difference' => 0,
|
||
|
],
|
||
|
'ctr' => [
|
||
|
'total' => 0,
|
||
|
'difference' => 0,
|
||
|
],
|
||
|
'pageviews' => [
|
||
|
'total' => 0,
|
||
|
'difference' => 0,
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 top 5 winning keywords.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_winning_keywords() {
|
||
|
$cache_key = Stats::get()->get_cache_key( 'winning_keywords', Stats::get()->days . 'days' );
|
||
|
$cache = get_transient( $cache_key );
|
||
|
|
||
|
if ( false !== $cache ) {
|
||
|
return $cache;
|
||
|
}
|
||
|
|
||
|
// Get most recent day's keywords only.
|
||
|
$keywords = $this->get_recent_keywords();
|
||
|
$keywords = wp_list_pluck( $keywords, 'query' );
|
||
|
$keywords = array_map( 'strtolower', $keywords );
|
||
|
$data = Stats::get()->get_analytics_data(
|
||
|
[
|
||
|
'order' => 'ASC',
|
||
|
'dimension' => 'query',
|
||
|
'where' => 'WHERE COALESCE( ROUND( t1.position - COALESCE( t2.position, 100 ), 0 ), 0 ) < 0',
|
||
|
]
|
||
|
);
|
||
|
$history = $this->get_graph_data_for_keywords( \array_keys( $data ) );
|
||
|
$data = Stats::get()->set_query_position( $data, $history );
|
||
|
|
||
|
set_transient( $cache_key, $data, DAY_IN_SECONDS );
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get top 5 losing keywords.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_losing_keywords() {
|
||
|
$cache_key = Stats::get()->get_cache_key( 'losing_keywords', Stats::get()->days . 'days' );
|
||
|
$cache = get_transient( $cache_key );
|
||
|
|
||
|
if ( false !== $cache ) {
|
||
|
return $cache;
|
||
|
}
|
||
|
|
||
|
// Get most recent day's keywords only.
|
||
|
$keywords = $this->get_recent_keywords();
|
||
|
$keywords = wp_list_pluck( $keywords, 'query' );
|
||
|
$keywords = array_map( 'strtolower', $keywords );
|
||
|
|
||
|
$data = Stats::get()->get_analytics_data(
|
||
|
[
|
||
|
'dimension' => 'query',
|
||
|
'where' => 'WHERE COALESCE( ROUND( t1.position - COALESCE( t2.position, 100 ), 0 ), 0 ) > 0',
|
||
|
]
|
||
|
);
|
||
|
$history = $this->get_graph_data_for_keywords( \array_keys( $data ) );
|
||
|
$data = Stats::get()->set_query_position( $data, $history );
|
||
|
|
||
|
set_transient( $cache_key, $data, DAY_IN_SECONDS );
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get keywords graph data.
|
||
|
*
|
||
|
* @param array $keywords Keywords to get data for.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_graph_data_for_keywords( $keywords ) {
|
||
|
global $wpdb;
|
||
|
|
||
|
$intervals = Stats::get()->get_intervals();
|
||
|
$sql_daterange = Stats::get()->get_sql_date_intervals( $intervals );
|
||
|
$keywords = \array_map( 'esc_sql', $keywords );
|
||
|
$keywords = '(\'' . join( '\', \'', $keywords ) . '\')';
|
||
|
|
||
|
$query = $wpdb->prepare(
|
||
|
"SELECT a.query, a.position, t.date, t.range_group
|
||
|
FROM {$wpdb->prefix}rank_math_analytics_gsc AS a
|
||
|
INNER JOIN
|
||
|
(SELECT query, DATE_FORMAT(created, '%%Y-%%m-%%d') as date, MAX(id) as id, {$sql_daterange}
|
||
|
FROM {$wpdb->prefix}rank_math_analytics_gsc
|
||
|
WHERE created BETWEEN %s AND %s
|
||
|
AND query IN {$keywords}
|
||
|
GROUP BY query, range_group
|
||
|
ORDER BY created ASC) AS t ON a.id = t.id
|
||
|
",
|
||
|
Stats::get()->start_date,
|
||
|
Stats::get()->end_date
|
||
|
);
|
||
|
$data = $wpdb->get_results( $query );
|
||
|
// phpcs:enable
|
||
|
|
||
|
$data = Stats::get()->filter_graph_rows( $data );
|
||
|
|
||
|
return array_map( [ Stats::get(), 'normalize_graph_rows' ], $data );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get pages by keyword.
|
||
|
*
|
||
|
* @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_keyword_pages( WP_REST_Request $request ) {
|
||
|
global $wpdb;
|
||
|
|
||
|
$query = $wpdb->prepare(
|
||
|
"SELECT DISTINCT g.page
|
||
|
FROM {$wpdb->prefix}rank_math_analytics_gsc as g
|
||
|
WHERE g.query = %s AND g.created BETWEEN %s AND %s
|
||
|
ORDER BY g.created DESC
|
||
|
LIMIT 5",
|
||
|
$request->get_param( 'query' ),
|
||
|
Stats::get()->start_date,
|
||
|
Stats::get()->end_date
|
||
|
);
|
||
|
|
||
|
$data = $wpdb->get_results( $query ); // phpcs:ignore
|
||
|
$pages = wp_list_pluck( $data, 'page' );
|
||
|
$console = Stats::get()->get_analytics_data(
|
||
|
[
|
||
|
'objects' => true,
|
||
|
'pageview' => true,
|
||
|
'sub_where' => " AND page IN ('" . join( "', '", $pages ) . "')",
|
||
|
]
|
||
|
);
|
||
|
return $console;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add focus keywords to Rank Tracker.
|
||
|
*
|
||
|
* @param int $post_id Post ID.
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function add_post_focus_keyword( $post_id ) {
|
||
|
if ( wp_is_post_revision( $post_id ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$auto_add_fks = Helper::get_settings( 'general.auto_add_focus_keywords', [] );
|
||
|
if (
|
||
|
empty( $auto_add_fks['enable_auto_import'] ) ||
|
||
|
empty( $auto_add_fks['post_types'] ) ||
|
||
|
! in_array( get_post_type( $post_id ), $auto_add_fks['post_types'], true )
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$focus_keyword = Helper::get_post_meta( 'focus_keyword', $post_id );
|
||
|
if ( empty( $focus_keyword ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$keywords_data = [];
|
||
|
$keywords = explode( ',', $focus_keyword );
|
||
|
if ( ! empty( $auto_add_fks['secondary_keyword'] ) ) {
|
||
|
$keywords_data = $keywords;
|
||
|
} else {
|
||
|
$keywords_data[] = current( $keywords );
|
||
|
}
|
||
|
|
||
|
DB::bulk_insert_query_focus_keyword_data( $keywords_data );
|
||
|
}
|
||
|
}
|