Как правильно интегрировать треккеры Google Analitics и Yandex Metrika

Способность к анализу не следует смешивать с простой изобретательностью, ибо аналитик всегда изобретателен, тогда как не всякий изобретательный человек способен к анализу.

Эдгар Аллан По

Введение

Существуют два способа решения данной задачи: на уровне темы или на уровне модуля, чем они отличаются? В данной статье мы постараемся разобраться, а так же подчеркну, что данный способ подходит и для других трекер-сервисов.

На уровне темы оформления трекеры будут работать только в её пределах, то есть при включении другой темы оформления или переходе (например в админку сайта) трекеры уже работать не будут, либо для каждой темы оформления нужно будет делать настройки.

На уровне модуля - трекеры будут интегрироваться в любую тему оформления.

Выбор способа реализации зависит исключительно от Ваших потребностей, если Вам достаточно отслеживать главную и типовые страницы - то Вам подойдет первый способ, если же Вы хотите отслеживать действия зарегистрированных пользователей, модераторов и администраторов - то Вам лучше подойдет второй способ.

В теме оформления

Для начала добавим код в тему оформления Вашего сайта:

your_theme.theme

/**
 * Implements hook_preprocess_html().
 */
function your_theme_preprocess_html(&$variables) {
  $account = \Drupal::currentUser();

  $roles = $account->getRoles();

  foreach ($roles as $role) {
    $variables['curretn_user_roles'][] = $role;
    $variables['attributes']['class'][] = 'role-' . $role;
  }

  $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple();
  $roles_names = array_combine(array_keys($roles), array_map(function($a){ return $a->label();}, $roles));

  foreach ($roles_names as $role => $name) {
    $variables['tracker_roles'][$role] = theme_get_setting($role);
  }

  $variables['top_traker'] = theme_get_setting('top_traker');
  $variables['bottom_traker'] = theme_get_setting('bottom_traker');
}

/**
 * Implements hook_form_system_theme_settings_alter().
 */
function your_theme_form_system_theme_settings_alter(&$form, FormStateInterface &$form_state, $form_id = NULL) {

  $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple();
  $roles_names = array_combine(array_keys($roles), array_map(function($a){ return $a->label();}, $roles));

  $form['tracker_roles'] = [
    '#type' => 'details',
    '#title' => t('Отслеживание трекером'),
    '#open' => TRUE,
    '#description' => t('Выберите роли которые будут отслеживаться трекерами.'),
  ];

  foreach ($roles_names as $role => $name) {
    $form['tracker_roles'][$role] = [
      '#type' => 'checkbox',
      '#title' => t($name),
      '#default_value' => theme_get_setting($role),
    ];
  }

  $form['tracker_roles']['top_traker'] = [
    '#type' => 'textarea',
    '#title' => t('Трекер в HEAD'),
    '#default_value' => theme_get_setting('top_traker'),
    '#description' => t("Введите JS или HTML код трекера."),
  ];

  $form['tracker_roles']['bottom_traker'] = [
    '#type' => 'textarea',
    '#title' => t('Трекер внизу BODY'),
    '#default_value' => theme_get_setting('bottom_traker'),
    '#description' => t("Введите JS или HTML код трекера."),
  ];
}

Теперь у нас в настройках темы появились необходимые поля и чекбоксы, отметьте те роли за которыми будет вестись отслеживание и введите коды трекеров:

Снимок экрана

Далее создадим или отредактируем файл html.html.twig в Вашей теме:

html.html.twig

{% set tracker_enable = 0 %} {# добавляемая строка #}
{% for role, status in tracker_roles %} {# добавляемая строка #}
  {% if status == 1 and role in curretn_user_roles %} {# добавляемая строка #}
    {% set tracker_enable = 1 %} {# добавляемая строка #}
  {% endif %} {# добавляемая строка #}
{% endfor %} {# добавляемая строка #}
<!DOCTYPE html>
<html{{ html_attributes }}>
  <head>
    <meta name="yandex-verification" content="237db23dff154515">
    <head-placeholder token="{{ placeholder_token }}">
    <title>{{ head_title|safe_join(' | ') }}</title>
    <css-placeholder token="{{ placeholder_token }}">
    <js-placeholder token="{{ placeholder_token }}">
    {% if tracker_enable == 1 %} {# добавляемая строка #}
      {{ top_traker|raw }} {# добавляемая строка #}
    {% endif %} {# добавляемая строка #}
  </head>
  <body{{ attributes.addClass(body_classes) }}>
    {{ page_top }}
    {{ page }}
    {{ page_bottom }}
    <js-bottom-placeholder token="{{ placeholder_token }}">
    {% if tracker_enable == 1 %} {# добавляемая строка #}
      {{ bottom_traker|raw }} {# добавляемая строка #}
    {% endif %} {# добавляемая строка #}
  </body>
</html>

В модуле

your_module.info.yml

name: YOUR Module
description: Your Module.
package: Administration # указываем группу модуля в которой он будет отображаться на страницеуправления модулями.

type: module
core: 8.x
core_version_requirement: ^8 || ^9 # указываем режим совместимости.

your_module.links.menu.yml

your_module.admin_settings_form: # задаем параметры ссылки в админке для модуля.
  title: 'YOUR настройки'
  route_name: your_module.admin_settings_form
  description: 'Настройки YOUR модуля'
  parent: system.admin_config
  weight: 99

your_module.routing.yml

your_module.admin_settings_form:
  path: '/admin/config/your_module/settings' # путь до страницы с настройками модуля.
  defaults:
    _form: '\Drupal\iqis_module\Form\SettingsYourModuleForm' # форма с настройками модуля.
    _title: 'Настройки YOUR модуля'
  requirements:
    _permission: 'access administration pages' # даем возможность на правку и просмотр только Администраторам.
  options:
    _admin_route: TRUE

your_module.module

<?php

/**
 * @file
 * Your Module.
 */

/**
 * Implements hook_page_attachments().
 */
function your_module_page_attachments(array &$page) {

  $account = \Drupal::currentUser(); // получаем данные о текущем пользователе
  $userRoles = $account->getRoles(); // получаем все роли назначенные пользователю

  $javascript_header = \Drupal::config('your_module.settings')->get('traker_top'); // загружаем код трекера для HEAD
  $roles = \Drupal::config('your_module.settings')->get('traker'); // загружаем список ролей которые будут отслеживаться

  $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple(); // получаем весь список ролей на сайте
  $roles_names = array_combine(array_keys($roles), array_map(function($a){ return $a->label();}, $roles)); // формируем массив с названиями ролей

  foreach ($roles_names as $role => $name) {
    $allowedRoles[$role ] = \Drupal::config('your_module.settings')->get($name);
  }

  $enebleTraker = 0; // устанавливаем переменную для контроля инициализации трекера

  foreach ($allowedRoles as $role => $name) { // перебираем массив с ролями и при совпадении устанавливаем переменную для контроля трекера в значение 1
    if (in_array($role, $userRoles) && $name == 1) {
      $enebleTraker = 1;
    }
  }

  if ($enebleTraker == 1) { // если переменная для контроля трекера равна 1 включаем код трекера в HEAD
    $page['#attached']['html_head'][] = [
      [
        '#tag' => 'script',
        '#value' => $javascript_header,
        '#weight' => 1,
      ],
      'traker'
    ];
  }

}

/**
 * Implements hook_preprocess_html().
 */
function your_module_preprocess_html(array &$variables) {
  $account = \Drupal::currentUser(); // получаем данные о текущем пользователе
  $userRoles = $account->getRoles(); // получаем все роли назначенные пользователю

  $javascript_footer = \Drupal::config('your_module.settings')->get('traker_bottom'); // загружаем код трекера для BODY
  $roles = \Drupal::config('your_module.settings')->get('traker'); // загружаем список ролей которые будут отслеживаться

  $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple(); // получаем весь список ролей на сайте
  $roles_names = array_combine(array_keys($roles), array_map(function($a){ return $a->label();}, $roles)); // формируем массив с названиями ролей

  foreach ($roles_names as $role => $name) {
    $allowedRoles[$role ] = \Drupal::config('your_module.settings')->get($name);
  }

  $enebleTraker = 0; // устанавливаем переменную для контроля инициализации трекера

  foreach ($allowedRoles as $role => $name) { // перебираем массив с ролями и при совпадении устанавливаем переменную для контроля трекера в значение 1
    if (in_array($role, $userRoles) && $name == 1) {
      $enebleTraker = 1;
    }
  }

  if ($enebleTraker == 1) { // если переменная для контроля трекера равна 1 включаем код трекера в BODY
    $variables['page_bottom'] = \Drupal\Core\Render\Markup::create($javascript_footer);
  }
}

Создаем папки src в ней создаем папку Form и уже в ней создаем файл SettingsYourModuleForm.php

SettingsYourModuleForm.php

<?php

namespace Drupal\your_module\Form;

/**
 * @file
 * Contains Drupal\your_module\Form\SettingsYourModuleForm.
 */

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * SettingsYourModuleForm().
 */
class SettingsYourModuleForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'your_module.settings',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'your_module_form';
  }

  /**
   * {@inheritdoc} конструируем форму с нужными нам полями.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('your_module.settings');

    $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple();
    $roles_names = array_combine(array_keys($roles), array_map(
      function ($a) {
        return $a->label();
      },
    $roles));

    $form['tracker_roles'] = [
      '#type' => 'details',
      '#title' => $this->t('Отслеживание трекером'),
      '#open' => TRUE,
      '#description' => t('Выберите роли которые будут отслеживаться трекерами.'),
    ];

    foreach ($roles_names as $role => $name) {
      $form['tracker_roles'][$role] = [
        '#type' => 'checkbox',
        '#title' => $name,
        '#default_value' => $config->get($name),
      ];
    }

    $form['tracker_roles']['traker_top'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Трекер в HEAD'),
      '#default_value' => $config->get('traker_top'),
      '#description' => $this->t("Введите JS или HTML код трекера."),
    ];

    $form['tracker_roles']['traker_bottom'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Трекер в BODY'),
      '#default_value' => $config->get('traker_bottom'),
      '#description' => $this->t("Введите JS или HTML код трекера."),
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc} сохраняем значения полей в форме.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);

    $roles = \Drupal::entityTypeManager()->getStorage('user_role')->loadMultiple();
    $roles_names = array_combine(array_keys($roles), array_map(
      function ($a) {
        return $a->label();
      },
    $roles));

    $this->config('your_module.settings')->set('traker_top', $form_state->getValue('traker_top'))->save();
    $this->config('your_module.settings')->set('traker_bottom', $form_state->getValue('traker_bottom'))->save();
    foreach ($roles_names as $role => $name) {
      $this->config('your_module.settings')->set($name, $form_state->getValue($role))->save();
    }
  }

}

В итоге по адресу your-site.ru/admin/config/your_module/settings Вы увидите созданную форму:

Снимок экрана

Внимание! Так как Twig обработчик будет оборачивать весь код в тег <script></script>, то Вам необходимо из кода счетчика убрать комментарии <!-- -->, первый тег <script> и последний тег </script>

Если Вы захотите использовать чистый код счетчика обернутый в <scripts> и комментарии, то вот патч который добавлет тег no_tag, и используя его '#tag' => 'script', вы сможете вставлять чистый код. Внимание! после обновления Drupal патч придется применять снова! Протестировано на Drupal 9.0.1

Если у Вас появились вопросы, не стесняйтесь пишите мне!
Оставьте Ваш комментарий