ga_reports-8.x-1.0/src/Plugin/views/query/GaQuery.php
src/Plugin/views/query/GaQuery.php
<?php
namespace Drupal\ga_reports\Plugin\views\query;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Component\Utility\Html;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a Views query class for Google Analytics Reports API.
*
* @ViewsQuery(
* id = "ga_query",
* title = @Translation("Google Analytics Query"),
* help = @Translation("Defines a Views query class for Google Analytics Reports API.")
* )
*/
class GaQuery extends QueryPluginBase {
/**
* A list of tables in the order they should be added, keyed by alias.
*
* @var tableQueue
*/
protected $tableQueue = [];
/**
* An array of fields.
*
* @var fields
*/
protected $fields = [];
/**
* An array mapping table aliases and field names to field aliases.
*
* @var fieldAliases
*/
protected $fieldAliases = [];
/**
* An array of sections of the WHERE query.
*
* Each section is in itself an array of pieces and a flag as to whether
* or not it should be AND or OR.
*
* @var where
*/
protected $where = [];
/**
* A simple array of order by clauses.
*
* @var orderby
*/
protected $orderby = [];
/**
* The default operator to use when connecting the WHERE groups.
*
* @var groupOperator
*/
protected $groupOperator = 'AND';
/**
* Module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
public $moduleHandler;
/**
* Config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
public $configFactory;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->moduleHandler = $module_handler;
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('module_handler'),
$container->get('config.factory')
);
}
/**
* Constructor; Create the basic query object and fill with default values.
*
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->unpackOptions($this->options, $options);
}
/**
* Add a metric or dimension to the query.
*
* @string $table
* NULL in most cases, we could probably remove this altogether.
* @string $field
* The name of the metric/dimension/field to add.
* @string $alias
* Probably could get rid of this too.
*
* @array $params
* Probably could get rid of this too.
*
* @return string
* The name that this field can be referred to as.
*/
public function addField($table, $field, $alias = '', $params = []) {
// We check for this specifically because it gets a special alias.
if ($table == $this->view->storage->get('base_table') && $field == $this->view->storage->get('base_field') && empty($alias)) {
$alias = $this->view->storage->get('base_field');
}
if ($table && empty($this->tableQueue[$table])) {
$this->ensureTable($table);
}
if (!$alias && $table) {
$alias = $table . '_' . $field;
}
// Make sure an alias is assigned.
$alias = $alias ? $alias : $field;
// We limit the length of the original alias up to 60 characters
// to get a unique alias later if its have duplicates.
$alias = substr($alias, 0, 60);
// Create a field info array.
$field_info = [
'field' => $field,
'table' => $table,
'alias' => $alias,
] + $params;
// Test to see if the field is actually the same or not. Due to
// differing parameters changing the aggregation function, we need
// to do some automatic alias collision detection:
$base = $alias;
$counter = 0;
while (!empty($this->fields[$alias]) && $this->fields[$alias] != $field_info) {
$field_info['alias'] = $alias = $base . '_' . ++$counter;
}
if (empty($this->fields[$alias])) {
$this->fields[$alias] = $field_info;
}
// Keep track of all aliases used.
$this->fieldAliases[$table][$field] = $alias;
return $alias;
}
/**
* Add a filter string to the query.
*
* @param string $group
* The filter group to add these to; groups are used to create AND/OR
* sections of the Google Analytics query. Groups cannot be nested.
* Use 0 as the default group. If the group does not yet exist it will
* be created as an AND group.
* @param string $field
* The name of the metric/dimension/field to check.
* @param mixed $value
* The value to test the field against. In most cases, this is a scalar.
* @param string $operator
* The comparison operator, such as =, <, or >=.
*/
public function addWhere($group, $field, $value = NULL, $operator = NULL) {
// Ensure all variants of 0 are actually 0. Thus '', 0 and NULL are all
// the default group.
if (empty($group)) {
$group = 0;
}
// Check for a group.
if (!isset($this->where[$group])) {
$this->setWhereGroup('AND', $group);
}
$this->where[$group]['conditions'][] = [
'field' => $field,
'value' => $value,
'operator' => $operator,
];
}
/**
* Add SORT attribute to the query.
*
* @string $table
* NULL, don't use this.
* @string $field
* The metric/dimensions/field.
* @string $order
* Either '' for ascending or '-' for descending.
* @string $alias
* Don't use this yet (at all?).
* @array $params
* Don't use this yet (at all?).
*/
public function addOrderBy($table, $field = NULL, $order = 'ASC', $alias = '', $params = []) {
$this->orderby[] = [
'field' => $field,
'direction' => (strtoupper($order) == 'DESC') ? '-' : '',
];
}
/**
* {@inheritdoc}
*/
public function query($get_count = FALSE) {
$available_fields = ga_reports_get_fields();
$query = [];
foreach ($this->fields as $field) {
$field_name = ga_reports_variable_to_custom_field($field['field']);
if ($available_fields[$field_name]) {
$type = $available_fields[$field_name]->type;
$type = ($type == 'dimension') ? 'dimensions' : 'metrics';
$query[$type][] = 'ga:' . $field['field'];
}
}
$filters = [];
if (isset($this->where)) {
foreach ($this->where as $where_group => $where) {
foreach ($where['conditions'] as $condition) {
$field_name = ga_reports_variable_to_custom_field($condition['field']);
if ($field_name == '.start_date' || $field_name == '.end_date' || $field_name == 'profile_id') {
// Remove dot from begging of the string.
$field_name = ltrim($field_name, '.');
$query[$field_name] = intval($condition['value']);
}
elseif (!empty($available_fields[$field_name])) {
$filters[$where_group][] = 'ga:' . $condition['field'] . $condition['operator'] . $condition['value'];
}
}
if (!empty($filters[$where_group])) {
$glue = ($where['type'] == 'AND') ? ';' : ',';
$filters[$where_group] = implode($glue, $filters[$where_group]);
}
}
}
if (!empty($filters)) {
$glue = ($this->groupOperator == 'AND') ? ';' : ',';
$query['filters'] = implode($glue, $filters);
}
if (isset($this->orderby)) {
foreach ($this->orderby as $field) {
$query['sort_metric'][] = $field['direction'] . 'ga:' . $field['field'];
}
}
// Change reports profile.
if (isset($this->options['reports_profile']) && (!empty($this->options['profile_id']))) {
$query['profile_id'] = $this->options['profile_id'];
}
return $query;
}
/**
* {@inheritdoc}
*/
public function alter(ViewExecutable $view) {
$this->moduleHandler->invokeAll('views_query_alter', [$view, $this]);
}
/**
* Builds the necessary info to execute the query.
*/
public function build(ViewExecutable $view) {
// Store the view in the object to be able to use it later.
$this->view = $view;
$view->initPager();
// Let the pager modify the query to add limits.
$view->pager->query();
$view->build_info['query'] = $this->query();
$view->build_info['count_query'] = $this->query(TRUE);
}
/**
* {@inheritdoc}
*/
public function execute(ViewExecutable $view) {
// Initial check to see if we should attempt to run the query.
if (!$this->configFactory->get('ga_reports.settings')->get('access_token')) {
// Optionally do not warn users on every query attempt before auth.
drupal_set_message(t('You must @link Drupal to use your Google Analytics account before you can view reports.', ['@link' => Link::createFromRoute('Authorize', 'ga_reports.settings')]));
return;
}
$query = $view->build_info['query'];
$count_query = $view->build_info['count_query'];
$start = microtime(TRUE);
// Query for total number of items.
$count_query['max_results'] = 9999;
$count_query['start_index'] = 1;
$count_feed = ga_reports_report_data($count_query);
// Process only if data is available.
if (!empty($count_feed->results->rows)) {
$view->pager->total_items = count($count_feed->results->rows);
$view->pager->updatePageInfo();
// Adjust based on the pager's modifications to limit and offset.
if (!empty($this->limit) || !empty($this->offset)) {
$query['max_results'] = intval(!empty($this->limit) ? $this->limit : 1000);
$query['start_index'] = intval(!empty($this->offset) ? $this->offset : 0) + 1;
}
$feed = ga_reports_report_data($query);
$rows = $feed->results->rows;
$views_result = [];
$count = 0;
foreach ($rows as $row) {
$count++;
$row['index'] = $count;
$views_result[] = new ResultRow($row);
}
$view->result = isset($views_result) ? $views_result : [];
$view->execute_time = microtime(TRUE) - $start;
if ($view->pager->usePager()) {
$view->total_rows = $view->pager->getTotalItems();
}
// Add to build_info['query'] to render query in Views UI query summary
// area.
$view->build_info['query'] = print_r($feed->results->query, TRUE);
}
else {
// Set empty query instead of current query array to prevent error
// in Views UI.
$view->build_info['query'] = '';
// Display the error from Google.
if (!empty($count_feed->response->data)) {
$response_data = json_decode($count_feed->response->data);
if (isset($response_data['error']['message'])) {
drupal_set_message(Html::escape($response_data['error']['message']), 'error');
}
}
}
}
/**
* {@inheritdoc}
*/
public function defineOptions() {
$options = parent::defineOptions();
// Load profiles list.
$profile_list = ga_reports_profiles_list();
if ($profile_list) {
$options['reports_profile'] = [
'default' => FALSE,
'translatable' => FALSE,
'bool' => TRUE,
];
$options['profile_id'] = [
'default' => $profile_list['profile_id'],
];
}
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
// Load profiles list.
$profile_list = ga_reports_profiles_list();
$profile_info = '';
if (isset($profile_list['current_profile'])) {
$profile_info = parse_url($profile_list['current_profile']->websiteUrl, PHP_URL_HOST) . ' - ' . $profile_list['current_profile']->name . ' (' . $profile_list['current_profile']->id . ')';
}
if ($profile_list) {
$form['reports_profile'] = [
'#title' => t('Use another reports profile'),
'#description' => t('This view will use another reports profile rather than system default profile: %profile.', [
'%profile' => $profile_info,
]),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['reports_profile']),
];
$form['profile_id'] = [
'#type' => 'select',
'#title' => t('Reports profile'),
'#options' => $profile_list['options'],
'#description' => t('Choose your Google Analytics profile.'),
'#default_value' => $this->options['profile_id'],
'#dependency' => ['edit-query-options-reports-profile' => '1'],
];
}
}
/**
* Make sure table exists.
*
* @param string $table
* Table name.
* @param string $relationship
* Relationship.
* @param string $join
* Join.
*/
public function ensureTable($table, $relationship = NULL, $join = NULL) {
}
}
