В этой заметке опишу свой опыт по регистрации AJAX действий в счётчике Яндекс.Метрика, а также приведу пример использования замыканий в PHP при разработке плагинов WordPress.

Итак, задача максимально проста. Если мы какой-либо контент подгружаем через AJAX (пример — “Докатился до AJAX, или WordPress + AJAX + jQuery: загружаем продолжение статьи (после <!—more—>)”), было бы правильным отразить этот факт и в счётчике / счётчиках. Поэтому и решил для начала написать простенький jQuery плагин, являющийся точкой интерфейса между счётчиками и генераторами AJAX событий. Итак:

jQuery плагин jQuery.AJAX.counters

А вот и его код:

jQuery.fn.AJAXCounters = function(method, arg) {
    var methods = {
        bind: function(id, counter) {
            $('body')
                .delegate(
                    '',
                    'hit.counter.' + id,
                    {
                        url: $(location).attr('href'),
                        title: $(document).attr('title'),
                        referrer: $(document).attr('referrer'),
                        params: {}
                    },
                    function (e, params) {
                        params = $.extend(e.data, params);
                        if ($.isFunction(counter.hit))
                            counter.hit(params.url, params.title, params.referrer, params.params);
                        return true;
                    }
                )
                .delegate(
                    '',
                    'reachGoal.counter.' + id,
                    {
                        target: '',
                        params: {}
                    },
                    function (e, params) {
                        params = $.extend(e.data, params);
                        if ($.isFunction(counter.reachGoal))
                            counter.reachGoal(params.target, params.params);
                        return true;
                    }
                )
            ;
            return this;
        },
        unbind: function(id) {
            $('body').undelegate(id);
            return this;
        },
        hit: function(params) {
            $('body').trigger('hit.counter', params);
            return this;
        },
        reachGoal: function(params) {
            $('body').trigger('reachGoal.counter', params);
            return this;
        }
    };

    if ( methods[method] ) {
        return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof arg === 'object' ) {
        return methods.bind( method, arg );
    } else {
        $.error( 'Метод "' +  method + '" не найден в плагине jQuery.AJAXcounters' );
    };
};

jQuery( function() {
    jQuery().AJAXCounters('hit');
});

Как видно, наш простенький плагин сам после загрузки документа заставит “сработать” (hit) все установленные через него счётчики (последние 3 строки кода). Наш плагин предоставляет ряд методов. В том числе bind и unbind, ну и hit (ради него всё и начиналось). При регистрации обработчиков событий мы используем namespace события в терминологии jQuery, что существенно облегчает нам “отвязывание” счётчика (именно благодаря namespace код метода unbind настолько краток).

Использование механизма событий jQuery дало возможность другим плагинам взаимодействовать с нашим, не вызывая напрямую наши методы и не проверяя вообще доступности нашего API. Достаточно для изменённого элемента просто сгенерировать событие:

$(this).trigger('hit.counter');

Именно так и действуют мои AJAX плагины.

Регистрируем Yandex.Metrika через собственный jQuery плагин

А вот и пример использования выше приведённого плагина:

jQuery().AJAXCounters( 'YaMetrika', new Ya.Metrika({
    id: YaMetrikaConfig.ua,
    defer: true,
    accurateTrackBounce: true
}) );

По сути – один вызов, и счётчик Яндекс.Метрики инициализирован и привязан не только к “статическим”, но и к AJAX событиям. Первым параметром мы передаём наш личный id на ресурсе Яндекс.Метрики (скажем – id счётчика). Как мы его передаём из php в JS – увидим далее.

Также кратко будет выглядеть и “привязывание” Google Analytics. А что делать, если счётчик имеет иное API, либо мы хотим свой счётчик использовать? Ничего сложного. Ниже пример регистрации собственного счётчика, который на самом деле просто выбрасывает alert на экран:

jQuery().AJAXCounters( 'test', {
    hit: function () {
        alert('test');
    }
});

Выше приведённый код удобно использовать при отладке, в частности.

По сути – всё. Теперь приведу PHP код самого плагина ради двух задач: передача id счётчика клиенту, а также – демонстрация использования PHP замыканий при разработке интерфейса в консоли управления WordPress.

PHP, замыкания, и плагин AJAX.Counters

Начну сразу с кода:

<?php

$ajax_yandex_metrika_cfg = array(
    'name' => '',
    'ver' => '2.0.0',
    'namespace' => 'ajax_yandex_metrika',
    'folder' => dirname(plugin_basename(__FILE__)),
    'domain' => dirname(plugin_basename(__FILE__)),
    'path' => WP_PLUGIN_DIR . '/' . dirname(plugin_basename(__FILE__)) . '/',
    'url' => WP_PLUGIN_URL . '/' . dirname(plugin_basename(__FILE__)) . '/',
    'options' => array(),
    'options_id' => 'YandexMetrikaPP',
    'options_page_id' => 'ajax_yandex_metrika_options_page'
);

function ajax_yandex_metrika_validate_options($options) {
    if (!is_array($options)) {
        $options = array();
    };
    if ($options['position'] != 'footer')
        $options['position'] = 'head';

    return $options;
}

if (is_admin()) {
    include_once('admin\admin.php');
};

add_action('init', function () {
    global $ajax_yandex_metrika_cfg;
    $pluginDIR = $ajax_yandex_metrika_cfg['path'];
    $pluginURL = $ajax_yandex_metrika_cfg['url'];

    if(!is_admin()) {
        $ajax_yandex_metrika_cfg['options'] = ajax_yandex_metrika_validate_options(
            get_option($ajax_yandex_metrika_cfg['options_id'])
        );
        $ua = $ajax_yandex_metrika_cfg['options']["uastring"];
        $script_pos = ( 'footer' == ($ajax_yandex_metrika_cfg['options']['position']) );

        if (($ua != "") && (!current_user_can('edit_users') || $options["admintracking"]) && !is_preview()) {
//        if (true) { //for debug
            wp_register_script(
                'jquery.ajax.counters',
                $pluginURL . "jquery/ajax/counters/jquery.ajax.counters.js",
                array('jquery'),
                $ajax_yandex_metrika_cfg['ver'],
                $script_pos
            );
            wp_register_script(
                'yandex.metrika',
                'http://mc.yandex.ru/resource/watch.js',
                array(),
                $ajax_yandex_metrika_cfg['ver'],
                $script_pos
            );
            wp_register_script(
                'ajax-yandex-metrika',
                $pluginURL . "ajax-yandex-metrika.js",
                array('jquery', 'yandex.metrika', 'jquery.ajax.counters'),
                $ajax_yandex_metrika_cfg['ver'],
                $script_pos
            );
            wp_enqueue_script('jquery.ajax.counters');
            wp_enqueue_script('yandex.metrika');
            wp_enqueue_script('ajax-yandex-metrika');

            wp_localize_script( 'ajax-yandex-metrika', 'YaMetrikaConfig', array(
                'ua' => $ua
            ));
        };
    };
});

?>

Ну, во-первых, мы выделили весь код, касающийся консоли управления, в файл admin.php. И грузим его только при необходимости (что правильно с точки зрения производительности). Во-вторых, обратите внимание на вызов wp_localize_script. Именно данный код и формирует фрагмент javascript кода на нашей странице, через который мы передаём параметры нашему подгружаемому javascript коду. Выглядит сформированный код следующим образом:

<script type='text/javascript'>/*  */
var YaMetrikaConfig = {
    ua: "5009335"
};
/*  */</script>

Да, можно идти другим путём, и генерировать javascript файл через php, включая необходимые параметры прямо в файл сценария. Но в ряде случаев предложенное выше решение вполне устраивает.

Также можно не “подключать” watch.js непосредственно в html, а реализовать подключиние через наш javascript файл (чтобы сэкономить несколько десятков байт в html коде). Пример читаем в справке Яндек.Метрики. (Подробное описание API счётчика Вы можете почитать здесь).

А теперь — к admin.php и лямбда-функциям:

<?php

register_deactivation_hook ( $AJAX_Yandex_metrika, function () {
    global $ajax_yandex_metrika_cfg;
    unregister_setting(
        $ajax_yandex_metrika_cfg['namespace'],
        $ajax_yandex_metrika_cfg['options_id']
    );
});

register_uninstall_hook ( $AJAX_Yandex_metrika, 'ajax_yandex_metrika_uninstall');

function ajax_yandex_metrika_uninstall() {
    global $ajax_yandex_metrika_cfg;
    delete_option(
        $ajax_yandex_metrika_cfg['options_id']
    );
};

add_action ('init', function () {
    global $ajax_yandex_metrika_cfg;
    $ajax_yandex_metrika_cfg['options'] = ajax_yandex_metrika_validate_options(
        get_option($ajax_yandex_metrika_cfg['options_id'])
    );
    load_plugin_textdomain(
        $ajax_yandex_metrika_cfg['domain'],
        false,
        $ajax_yandex_metrika_cfg['folder'] . '/languages/'
    );
    $ajax_yandex_metrika_cfg['name'] = __('AJAX Yandex Metrika', $ajax_yandex_metrika_cfg['domain']);

    add_action('admin_init', function () {
        global $ajax_yandex_metrika_cfg;

        register_setting(
            $ajax_yandex_metrika_cfg['namespace'],
            $ajax_yandex_metrika_cfg['options_id'],
            'ajax_yandex_metrika_validate_options'
        );

        add_settings_section(
            $ajax_yandex_metrika_cfg['namespace'] . '_main_options',
            __('Main Settings', $ajax_yandex_metrika_cfg['domain']),
            function () {
                global $ajax_yandex_metrika_cfg;
                ?>
                <p>
                    <?php _e('You must obtain UID from <a href="http://metrika.yandex.ru/">yandex.metrika</a> and set it in the UID field.', $ajax_yandex_metrika_cfg['domain']); ?>
                </p>
                <?php
            },
            $ajax_yandex_metrika_cfg['options_page_id']
        );
        add_settings_field(
            $ajax_yandex_metrika_cfg['options_id'] . '[uastring]',
            __('Yandex.Metrika UID', $ajax_yandex_metrika_cfg['domain']),
            function () {
                global $ajax_yandex_metrika_cfg;
                ?>
                    <label>
                        <input
                            name="<?php echo $ajax_yandex_metrika_cfg['options_id'] . '[uastring]' ?>"
                            type="text"
                            maxlength="40"
                            style="width: 100%"
                            value="<?php echo $ajax_yandex_metrika_cfg['options']['uastring']; ?>"
                        />
                        <br/><?php _e('Your Yandex.Metrika UID.', $ajax_yandex_metrika_cfg['domain']); ?>
                    </label>
                <?php
            },
            $ajax_yandex_metrika_cfg['options_page_id'],
            $ajax_yandex_metrika_cfg['namespace'] . '_main_options'
        );

        add_settings_field(
            $ajax_yandex_metrika_cfg['options_id'] . '[position]',
            __('Code in the head', $ajax_yandex_metrika_cfg['domain']),
            function () {
                global $ajax_yandex_metrika_cfg;
            ?>
                <div>
                    <label>
                        <input
                            type="radio"
                            name="<?php echo $ajax_yandex_metrika_cfg['options_id'] . '[position]' ?>"
                            value="head"
                            <?php checked( $ajax_yandex_metrika_cfg['options']['position'], 'head' ); ?>
                        />
                        <?php _e('At the begin of pages (wp_head), when wp_footer isn`t used in the theme.', $ajax_yandex_metrika_cfg['domain']) ?>
                    </label>
                </div>
                <div>
                    <label>
                        <input
                            type="radio"
                            name="<?php echo $ajax_yandex_metrika_cfg['options_id'] . '[position]' ?>"
                            value="footer"
                            <?php checked( $ajax_yandex_metrika_cfg['options']['position'], 'footer' ); ?>
                        />
                        <?php _e('At the end of pages (wp_footer), by default.', $ajax_yandex_metrika_cfg['domain']) ?>
                    </label>
                </div>
            <?php
            },
            $ajax_yandex_metrika_cfg['options_page_id'],
            $ajax_yandex_metrika_cfg['namespace'] . '_main_options'
        );
    });

    add_action('admin_menu', function () {
        global $ajax_yandex_metrika_cfg;
        add_options_page(
            __('AJAX Yandex.Metrika', $ajax_yandex_metrika_cfg['domain'])
            , __('AJAX Yandex.Metrika', $ajax_yandex_metrika_cfg['domain'])
            , 'manage_options'
            , $ajax_yandex_metrika_cfg['options_page_id']
            , function () {
                global $ajax_yandex_metrika_cfg;
                ?>
                <div class="wrap">
                    <?php screen_icon('options-general'); ?>
                    <h2><?php _e('AJAX Yandex.Metrika options', $ajax_yandex_metrika_cfg['domain']) ?></h2>
                    <form method="post" action="options.php">
                        <?php
                            settings_fields($ajax_yandex_metrika_cfg['namespace']);
                            do_settings_sections($ajax_yandex_metrika_cfg['options_page_id']);
                        ?>
                        <p class="submit">
                        <input name="Submit" type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
                        </p>
                    </form>
                </div>
                <?php
            }
        );
    });
});

?>

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

P.S. Замыкания я не использовал сознательно, ограничился лямбда функциями. Дело в том, что мне требуется взаимодействие функционалов с одним разделяемым объектом параметров плагина, а не создание фиксированной копии параметров для каждого функционала в момент создания функционала. Посему замыкания здесь были бы вредны.

Качайте плагин AJAX Yandex.Metrika из репозитория WordPress и используйте его в своё удовольствие!

P.S. Текущая версия плагина (2.0.0) требует от Вас самостоятельной регистрации на Яндексе, получении id счётчика а также самостоятельного подтверждения прав на сайт. Однако, Яндекс.Метрика предоставляет API с авторизацией по  OAuth 2.0, с помощью которого можно автоматизировать процесс регистрации счётчика и подтверждения прав на сайт. Уж очень хочется попробовать реализовать полноценное взаимодействие по oAuth 2.0 и сделать плагин совсем для ленивых. Жаль, что пока что Яндекс.Метрика не поддерживает авторизации по OpenId, а то можно было бы и войти с правами текущего пользователя WordPress сайта…

Ну и кроме выше описанного также имеет смысл добавить в dashboard свой виджет с данными с Яндекс.Метрики.

Отзывы » (10)

  1. Добрый день, Сергей.В репозитарии WordPress на странице с вашим плагином  AJAX Yandex.Metrika указана неправильная ссылка на ваш блог:  For more information, please visit the Sergey S. Betke blog: http://sergey-s-betke.blogs.novgaro.ru/category/it/web/wordpress/ajax-yandex-metrika

  2. Здравствуйте! Спасибо за чудесный плагин. Попробовал его установить, но не нашел, где в итоге вводить настройки после его активации. В параметрах раздела с настройками плагина не нашел… помогите, пожалуйста, разобраться.

  3. При попытке установить плагин, скачанный с репозитория на wprdpress.org версия плагина 2.1.0, версия движка 3.3.1 при активации вылазит следующая ошибка:
    Parse error: syntax error, unexpected T_FUNCTION in /путь/wp-content/plugins/ajax-yandexmetrika/ajax-yandex-metrika.php on line 54 и активация  не происходит :(

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

Опубликовать комментарий

XHTML: Вы можете использовать следующие HTML теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Tags Связь с комментариями статьи:
RSS комментарии
Обратная ссылка