You are here

class GoogleAnalyticsQuery in Google Analytics Reports 8.3

Defines a Views query class for Google Analytics Reports API.

Plugin annotation


@ViewsQuery(
  id = "google_analytics_query",
  title = @Translation("Google Analytics Query"),
  help = @Translation("Defines a Views query class for Google Analytics Reports API.")
)

Hierarchy

Expanded class hierarchy of GoogleAnalyticsQuery

File

src/Plugin/views/query/GoogleAnalyticsQuery.php, line 28

Namespace

Drupal\google_analytics_reports\Plugin\views\query
View source
class GoogleAnalyticsQuery extends QueryPluginBase {
  use StringTranslationTrait;

  /**
   * A list of tables in the order they should be added, keyed by alias.
   *
   * @var array
   */
  protected $tableQueue = [];

  /**
   * An array of fields.
   *
   * @var array
   */
  protected $fields = [];

  /**
   * An array mapping table aliases and field names to field aliases.
   *
   * @var array
   */
  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 array
   */
  protected $where = [];

  /**
   * A simple array of order by clauses.
   *
   * @var array
   */
  protected $orderby = [];

  /**
   * The default operator to use when connecting the WHERE groups.
   *
   * @var string
   */
  protected $groupOperator = 'AND';

  /**
   * Module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  public $moduleHandler;

  /**
   * Config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  public $configFactory;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The state service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, StateInterface $state, MessengerInterface $messenger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->moduleHandler = $module_handler;
    $this->configFactory = $config_factory;
    $this->state = $state;
    $this->messenger = $messenger;
  }

  /**
   * {@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'), $container
      ->get('state'), $container
      ->get('messenger'));
  }

  /**
   * 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.
   *
   * @param string $table
   *   NULL in most cases, we could probably remove this altogether.
   * @param string $field
   *   The name of the metric/dimension/field to add.
   * @param string $alias
   *   Probably could get rid of this too.
   * @param 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 = '', array $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.
   *
   * @param string $table
   *   NULL, don't use this.
   * @param string $field
   *   The metric/dimensions/field.
   * @param string $order
   *   Either '' for ascending or '-' for descending.
   * @param string $alias
   *   Don't use this yet (at all?).
   * @param array $params
   *   Don't use this yet (at all?).
   */
  public function addOrderBy($table, $field = NULL, $order = 'ASC', $alias = '', array $params = []) {
    $this->orderby[] = [
      'field' => $field,
      'direction' => strtoupper($order) == 'DESC' ? '-' : '',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function query($get_count = FALSE) {
    $available_fields = google_analytics_reports_get_fields();
    $query = [];
    foreach ($this->fields as $field) {
      $field_name = google_analytics_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 = google_analytics_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 (!empty($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->state
      ->get('google_analytics_reports_api.access_token')) {

      // Optionally do not warn users on every query attempt before auth.
      $this->messenger
        ->addMessage($this
        ->t('You must <a href=":url">authorize your site</a> to use your Google Analytics account before you can view reports.', [
        ':url' => Url::fromRoute('google_analytics_reports_api.settings')
          ->toString(),
      ]));
      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 = google_analytics_reports_api_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 = google_analytics_reports_api_report_data($query);
      $rows = $feed->results->rows;
      $views_result = [];
      $count = 0;
      foreach ($rows as $row) {
        $row['index'] = $count;
        $views_result[] = new ResultRow($row);
        $count++;
      }
      $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'])) {
          $this->messenger
            ->addMessage(Html::escape($response_data['error']['message']), 'error');
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function defineOptions() {
    $options = parent::defineOptions();
    $options['reports_profile'] = [
      'default' => FALSE,
      'translatable' => FALSE,
      'bool' => TRUE,
    ];
    $options['profile_id'] = [
      'default' => FALSE,
    ];
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);

    // Load profiles list.
    $profile_list = google_analytics_reports_api_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' => $this
          ->t('Use another reports profile'),
        '#description' => $this
          ->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' => $this
          ->t('Reports profile'),
        '#options' => $profile_list['options'],
        '#description' => $this
          ->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) {
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
GoogleAnalyticsQuery::$configFactory public property Config factory.
GoogleAnalyticsQuery::$fieldAliases protected property An array mapping table aliases and field names to field aliases.
GoogleAnalyticsQuery::$fields protected property An array of fields.
GoogleAnalyticsQuery::$groupOperator protected property The default operator to use when connecting the WHERE groups.
GoogleAnalyticsQuery::$messenger protected property The messenger. Overrides MessengerTrait::$messenger
GoogleAnalyticsQuery::$moduleHandler public property Module handler.
GoogleAnalyticsQuery::$orderby protected property A simple array of order by clauses.
GoogleAnalyticsQuery::$state protected property The state service.
GoogleAnalyticsQuery::$tableQueue protected property A list of tables in the order they should be added, keyed by alias.
GoogleAnalyticsQuery::$where protected property An array of sections of the WHERE query.
GoogleAnalyticsQuery::addField public function Add a metric or dimension to the query.
GoogleAnalyticsQuery::addOrderBy public function Add SORT attribute to the query.
GoogleAnalyticsQuery::addWhere public function Add a filter string to the query.
GoogleAnalyticsQuery::alter public function Let modules modify the query just prior to finalizing it. Overrides QueryPluginBase::alter
GoogleAnalyticsQuery::build public function Builds the necessary info to execute the query. Overrides QueryPluginBase::build
GoogleAnalyticsQuery::buildOptionsForm public function Provide a form to edit options for this plugin. Overrides PluginBase::buildOptionsForm
GoogleAnalyticsQuery::create public static function Creates an instance of the plugin. Overrides PluginBase::create
GoogleAnalyticsQuery::defineOptions public function Information about options for all kinds of purposes will be held here. Overrides PluginBase::defineOptions
GoogleAnalyticsQuery::ensureTable public function Make sure table exists.
GoogleAnalyticsQuery::execute public function Executes the query and fills the associated view object with according values. Overrides QueryPluginBase::execute
GoogleAnalyticsQuery::init public function Constructor; Create the basic query object and fill with default values. Overrides PluginBase::init
GoogleAnalyticsQuery::query public function Generate a query and a countquery from all of the information supplied to the object. Overrides QueryPluginBase::query
GoogleAnalyticsQuery::__construct public function Constructs a PluginBase object. Overrides PluginBase::__construct
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$definition public property Plugins's definition
PluginBase::$displayHandler public property The display object this plugin is for.
PluginBase::$options public property Options for this plugin will be held here.
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::$renderer protected property Stores the render API renderer. 3
PluginBase::$usesOptions protected property Denotes whether the plugin has an additional options form. 8
PluginBase::$view public property The top object of a view. 1
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::destroy public function Clears a plugin. Overrides ViewsPluginInterface::destroy 2
PluginBase::doFilterByDefinedOptions protected function Do the work to filter out stored options depending on the defined options.
PluginBase::filterByDefinedOptions public function Filter out stored options depending on the defined options. Overrides ViewsPluginInterface::filterByDefinedOptions
PluginBase::getAvailableGlobalTokens public function Returns an array of available token replacements. Overrides ViewsPluginInterface::getAvailableGlobalTokens
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::getProvider public function Returns the plugin provider. Overrides ViewsPluginInterface::getProvider
PluginBase::getRenderer protected function Returns the render API renderer. 1
PluginBase::globalTokenForm public function Adds elements for available core tokens to a form. Overrides ViewsPluginInterface::globalTokenForm
PluginBase::globalTokenReplace public function Returns a string with any core tokens replaced. Overrides ViewsPluginInterface::globalTokenReplace
PluginBase::INCLUDE_ENTITY constant Include entity row languages when listing languages.
PluginBase::INCLUDE_NEGOTIATED constant Include negotiated languages when listing languages.
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginBase::listLanguages protected function Makes an array of languages, optionally including special languages.
PluginBase::pluginTitle public function Return the human readable name of the display. Overrides ViewsPluginInterface::pluginTitle
PluginBase::preRenderAddFieldsetMarkup public static function Moves form elements into fieldsets for presentation purposes. Overrides ViewsPluginInterface::preRenderAddFieldsetMarkup
PluginBase::preRenderFlattenData public static function Flattens the structure of form elements. Overrides ViewsPluginInterface::preRenderFlattenData
PluginBase::queryLanguageSubstitutions public static function Returns substitutions for Views queries for languages.
PluginBase::setOptionDefaults protected function Fills up the options of the plugin with defaults.
PluginBase::themeFunctions public function Provide a full list of possible theme templates used by this style. Overrides ViewsPluginInterface::themeFunctions 1
PluginBase::trustedCallbacks public static function Lists the trusted callbacks provided by the implementing class. Overrides TrustedCallbackInterface::trustedCallbacks 6
PluginBase::unpackOptions public function Unpack options over our existing defaults, drilling down into arrays so that defaults don't get totally blown away. Overrides ViewsPluginInterface::unpackOptions
PluginBase::usesOptions public function Returns the usesOptions property. Overrides ViewsPluginInterface::usesOptions 8
PluginBase::validate public function Validate that the plugin is correct and can be saved. Overrides ViewsPluginInterface::validate 6
PluginBase::viewsTokenReplace protected function Replaces Views' tokens in a given string. The resulting string will be sanitized with Xss::filterAdmin. 1
PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT constant Query string to indicate the site default language.
QueryPluginBase::$limit protected property Stores the limit of items that should be requested in the query.
QueryPluginBase::$pager public property A pager plugin that should be provided by the display.
QueryPluginBase::addSignature public function Add a signature to the query, if such a thing is feasible. 1
QueryPluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides PluginBase::calculateDependencies 1
QueryPluginBase::getAggregationInfo public function Get aggregation info for group by queries. 1
QueryPluginBase::getCacheContexts public function The cache contexts associated with this object. Overrides CacheableDependencyInterface::getCacheContexts
QueryPluginBase::getCacheMaxAge public function The maximum age for which this object may be cached. Overrides CacheableDependencyInterface::getCacheMaxAge 1
QueryPluginBase::getCacheTags public function The cache tags associated with this object. Overrides CacheableDependencyInterface::getCacheTags 1
QueryPluginBase::getDateField public function Returns a Unix timestamp to database native timestamp expression. 1
QueryPluginBase::getDateFormat public function Creates cross-database date formatting. 1
QueryPluginBase::getEntityTableInfo public function Returns an array of all tables from the query that map to an entity type.
QueryPluginBase::getLimit public function Returns the limit of the query.
QueryPluginBase::getTimezoneOffset public function Get the timezone offset in seconds.
QueryPluginBase::loadEntities public function Loads all entities contained in the passed-in $results. . If the entity belongs to the base table, then it gets stored in $result->_entity. Otherwise, it gets stored in $result->_relationship_entities[$relationship_id]; 1
QueryPluginBase::setFieldTimezoneOffset public function Applies a timezone offset to the given field. 2
QueryPluginBase::setGroupOperator public function Control how all WHERE and HAVING groups are put together.
QueryPluginBase::setLimit public function Set a LIMIT on the query, specifying a maximum number of results.
QueryPluginBase::setOffset public function Set an OFFSET on the query, specifying a number of results to skip
QueryPluginBase::setupTimezone public function Set the database to the current user timezone. 1
QueryPluginBase::setWhereGroup public function Create a new grouping for the WHERE or HAVING clause.
QueryPluginBase::submitOptionsForm public function Handle any special handling on the validate form. Overrides PluginBase::submitOptionsForm 1
QueryPluginBase::summaryTitle public function Returns the summary of the settings in the display. Overrides PluginBase::summaryTitle
QueryPluginBase::validateOptionsForm public function Validate the options form. Overrides PluginBase::validateOptionsForm
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TrustedCallbackInterface::THROW_EXCEPTION constant Untrusted callbacks throw exceptions.
TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION constant Untrusted callbacks trigger silenced E_USER_DEPRECATION errors.
TrustedCallbackInterface::TRIGGER_WARNING constant Untrusted callbacks trigger E_USER_WARNING errors.