1 of 34

Integrated Analytics

Drupal 8 and the Statistics API

Eric Peterson (iamEAP)

2 of 34

Goals and takeaways of this session

  • Understand integrated analytics conceptually and get concrete implementation ideas.

  • Learn the fundamental concepts behind the Drupal 8 Statistics API

  • Concrete API implementation details

  • Get updated on API completion and ways to contribute, both big and small.

3 of 34

Background: Drupal Statistics

Core Statistics in Drupal is a legacy module from the early Drupal days. It’s cumbersome, inflexible, non-performant, and not very useful.

In D8, it serves only as a node hit counter.

The Statistics API in D8 is a contrib effort, stemming from lessons learned in the D7 Better Statistics module.

4 of 34

An illustrative example

5 of 34

...And we need a website

  • What kind of stuff do we want on our site?
    • Lyrics
    • Tour dates
    • Blog

  • I just installed this “Google Analytics” thing. What kind of analytics would be useful to us?
    • Top inbound referrers / keywords (e.g. is MySpace still a thing?)
    • Content popularity on lyric pages to inform setlists
    • Web traffic geo-origination for tour planning

6 of 34

...And we’ve decided on Bartik

7 of 34

Realigning priorities

  • What kind of stuff do we want on our site?
    • A store where people can buy our record and merchandise.
    • A place to stream previews of our record.

  • What kind of analytics would be useful to us?
    • Goal / conversion tracking for the above actions
    • Top inbound referrers filtered by people who actually converted above,
    • Geo-origination of visitors filtered by people who actually converted above.

8 of 34

Implementation using GA

<div class="playlist"><h2>Drew Paul and the Lorem Ipsums</h2><ol class="tracklist"><li><a class="play" href="#">(I Can't Get No) Dependency Injection</a></li><li><a class="play" href="#">Unchained Method Calls</a></li></ol></div>

(function($) {� $('.playlist a').click(function() {� var category = 'Playlist Play',� action = $(this).closest('.playlist').find('h2').text(),� label = $(this).text();� _gaq.push(['_trackEvent', category, action, label]);� });�})(jQuery);

9 of 34

Analytical capabilities

  • We can now segment our GA reports by people who actually listened to our record.
  • Not only who listened, but what particular album or song was most listened to.
  • Insightful, but we’ve missed an opportunity.
    • A statistically significant number of people who buy our record have, at some point in the past, listened to the record.

10 of 34

Reacting to analytics events

  • What if we also told Drupal of the event when sending it to GA?

(function($) {� $('.playlist a').click(function() {� var category = 'playlist play',� action = $(this).closest('.playlist').find('h2').text(),� label = $(this).text();� _gaq.push(['_trackEvent', category, action, label]);� SA('event', [category, action, label]);� });�})(jQuery);

  • And change the site experience based on that info...
    • We should promote store links more prominently to people who’ve listened to songs.
    • We should promote shows more prominently to people who’ve listened to songs, particularly when an upcoming show exists in their geography.

11 of 34

12 of 34

Defining integrated analytics

Integrated analytics is when your web application utilizes analytics data to respond to or otherwise dynamically alter a user’s web experience.

It is a fundamental piece to the puzzle of using Drupal to manage web experiences, not just content.

13 of 34

Integrated analytics building blocks

  • Arbitrary measures and dimensions
    • Browser, OS, Location, Screen Resolution...
    • Time on page, bounces, page load times...
  • A way to coherently bundle this data
    • Pageviews, visitors, API endpoint accesses
    • Events (link clicks, form submissions, scrolling...)
  • Data storage, management, access controls
  • Analysis and reporting capabilities

14 of 34

Drupal 8 Statistics API

  • Measures/dimensions via StatData plugins.

  • Bundles of StatData via StatMethod plugins

  • Flexible storage/access via Entity API:
    • Stat Methods are bundles of a new Stat Entity
    • Stat Data are properties attached to the Stat Entity

  • Analysis and reporting capabilities
    • Views integration

15 of 34

Drupal 8 Statistics API Concepts

  • SAPI introduces a new entity called “Stat”

  • Stat entities are bundled by “method” (which is a config entity).

  • Methods are declared/defined using StatMethod plugins; all business logic contained therein.

  • Methods also logically group entity properties.

  • Stat entity properties are defined using StatData plugins; all business logic contained therein.

16 of 34

Introduction to StatData plugins

Write StatData plugins so you can react to and analyze interesting data for which your contrib/custom module is responsible.

A StatData plugin is responsible for defining:

  • A dimension or measure (e.g. page URL)
  • A schema for this dimension/measure
  • A method to collect this data.
  • Optionally, forms/validation for any associated settings.

17 of 34

File: /my_module/lib/Drupal/Plugin/Stat/data/UserAgent.php

<?php

namespace Drupal\my_module\Plugin\Stat\data;�use Drupal\sapi\StatDataPluginBase;

use Drupal\sapi\StatDataPluginInterface;

/**� * Provides a user agent Statistics API data field.� *� * @StatData(� * id = "user_agent",� * label = @Translation("User-agent"),� * description = @Translation("User-agent string for the request."),� * help = @Translation(""),� * schema = {� * "type" = "varchar",� * "length" = 255,� * "not null" = FALSE,� * "description" = "User-agent string used on the request."� * }� * )� */class UserAgent extends StatDataPluginBase implements StatDataPluginInterface {

// See next slide for implementation...

18 of 34

Class definition as continued from previous slide, returning the user agent from the PHP global $_SERVER variable.

// Continued from previous slide...

/**� * Implements StatDataPluginInterface::execute()� * @return string

* Returns the user-agent for the given request. If none is set or none

* is provided, an empty string is returned.� */public function execute() {� if (isset($_SERVER['HTTP_USER_AGENT'])) {� return $_SERVER['HTTP_USER_AGENT'];� }� else {� return '';� }� }�}

19 of 34

Dependency injection and StatData

<?php

// Classes in addition to those used in previous slides.

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;�use Symfony\Component\HttpFoundation\Request;�

class UserAgent extends StatDataPluginBase implements ContainerFactoryPluginInterface {�

/**� * The current request.� * @var Symfony\Component\HttpFoundation\Request� */protected $request;

// Continued on next slide...

20 of 34

// Continued from previous slide...

/**� * Implements \Drupal\Core\Plugin\ContainerFactoryPluginInterface::create().� */public static function create(ContainerInterface $container, array $conf, $id, array $def) {� return new static($conf, $id, $def, $container->get('request'));� }

public function __construct(array $conf, $id, array $def, Request $request) {� // Save off our dependency to our protected “request” property. $this->request = $request;

parent::__construct($conf, $id, $def);

}

public function execute() {� if (isset($this->request->headers)) {� return $this->request->headers->get('user-agent', '');� }� else {� return '';� }� }�}

21 of 34

Introduction to StatMethod plugins

A StatMethod plugin is necessary to generate an entity bundle that can be used to store your StatData field data.

A StatMethod plugin is responsible for defining:

  • A data collection/execution implementation
  • Configuration forms/validation for settings
  • A way to restrict data collection if necessary
  • A periodic data management routine

Note: Nearly all of this is taken care of for you in a base class.

22 of 34

File: /my_module/lib/Drupal/my_module/Stat/method/Pageview.php

<?php��namespace Drupal\my_module\Plugin\Stat\method;�use Drupal\sapi\StatPluginMethodBase;

use Drupal\sapi\StatPluginMethodInterface;��/**� * Provides a Statistics API pageview method handler.� *� * @StatMethod(� * id = "pageview",� * label = @Translation("Pageview"),� * description = @Translation("Provides pageview statistics."),� * help = @Translation(""),� * status = "0"� * )� */class Pageview extends StatPluginMethodBase implements StatPluginMethodInterface {}

23 of 34

Overriding StatMethod plugins

1. StatPluginMethodInterface::collect()

  • Instantiates and saves a new Stat entity of your type.
  • Only overwrite if you aren’t using Entity storage for your data.
  • Also note the availability of preCollect() and postCollect()

2. StatPluginMethodInterface::methodForm()

  • Used to add custom plugin settings for just your plugin.
  • Also note methodValidate() and methodSubmit()

3. StatPluginMethodInterface::restrict()

  • Provides some default collection restrictions (DNT, role-based)
  • Extend this method to provide your own, custom restrictions.

24 of 34

4. StatPluginMethodInterface::manageData()

  • Periodically (e.g. on cron) performs management tasks
  • By default, removes data older than a configured interval
  • Can be extended to suit custom needs, calculations, etc.

25 of 34

Collecting data: invoking plugins

Once you have a Stat Method and a few Stat Data plugins defined. It’s time to use them.

<?php�// Load the SAPI plugin manager.$plugin = \Drupal::service('plugin.manager.sapi.method');�// Create an instance of our pageview method.$pageview = $plugin>createInstance('pageview');

// Call the execute() method to invoke collect data.$pageview->execute();

Use this anywhere in code to collect data.

Note: The DX around this is likely to change...

26 of 34

Collecting data: JavaScript API

For events that are purely client-side (like scrolling, clicking), a fully asynchronous, lightweight JavaScript API is available.

<html><head>�<script type="text/javascript">� // A manual call to track pageviews in GA� _gaq.push(['_trackPageview', someurl]);� // The equivalent SAPI call for our pageview method� SA('pageview');�</script>�</head>

More details in Better Statistics 7.x-1.x: https://drupal.org/project/better_statistics

27 of 34

Putting it all together

  • Arbitrary measures and dimensions

  • A way to coherently bundle, manage data

  • Data collection and storage

class MyData extends StatDataPluginBase implements StatDataPluginInterface {}

class MyMethod extends StatMethodPluginBase implements StatMethodPluginInterface {}

$plugin = \Drupal::service('plugin.manager.sapi.method');�$pageview = $plugin>createInstance('mymethod');�$pageview->execute();

<script type="text/javascript">SA('mymethod');</script>

28 of 34

Stat entity storage controllers

If you have a highly trafficked website (and need greater throughput), or you want to push your data to an external source, you can implement your own Stat storage controller.

This is still somewhat theoretical, but the goal is to allow users to configure storage backends within the SAPI GUI on a per-method basis.

Must implement StatStorageControllerInterface.

29 of 34

Putting together integration

<?php�$result = \Drupal::entityQuery('stat')� // Return Event Stats...->condition('method', 'event')� // For the current user...->condition('uid', \Drupal::currentUser()->id, '=')� // Where the event category is "playlist play"...->condition('event_category', 'playlist play')� ->sort('created', 'DESC')� ->execute();��if ($result) {� // The user's listened to one or more songs.$stat_data = entity_load('stat', array(key($result)));�}�else {

// The user hasn't listened to any of our songs yet.

}

Or just use Views. Or maybe SAPI will provide condition plugins.

30 of 34

The “analytics” problem

A Stat is an entity with “data” properties on it. So you’d make a view of Stat entities, and select the data properties you wanted to include.

Views UI is a terrible tool for data analysis, however. Instead, you may want to consider:

  • Additionally push data to GA, analyze there.
  • Additionally push/analyze data in ElasticSearch
  • Keep data as-is, connect/analyze with Tableau.

31 of 34

Goal: By Drupal 8.0 stable

  • Beta SAPI release or better w/frozen API.
    • Implementing appropriate data plugins for all core modules.

Additional modules implementing the API:

  • Alpha “Web Analytics” module (pageviews, events, bounce rates, goal tracking).
  • Alpha “Better Statistics” module

32 of 34

Ways to contribute

  • As you start porting your modules to and writing your modules for Drupal 8, consider at least adding StatData plugins for interesting data your module stores.

  • If you have a module that provides statistics, analytics, or content popularity functionality, consider whether or not you can simplify your module by utilizing SAPI components.

33 of 34

More direct contributions

If integrated analytics interests you or your organization could benefit, consider helping port/write the Statistics API or either of the aforementioned implementation modules.

https://drupal.org/project/sapi

https://drupal.org/project/better_statistics

34 of 34

Questions, discussion, resources

Drupal Projects:

https://drupal.org/project/sapi

https://drupal.org/project/better_statistics

Contact:

Twitter: iamEAP

Also everywhere else.