Integrated Analytics
Drupal 8 and the Statistics API
Eric Peterson (iamEAP)
Goals and takeaways of this session
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.
An illustrative example
...And we need a website
...And we’ve decided on Bartik
Realigning priorities
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);
Analytical capabilities
Reacting to analytics events
(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);
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.
Integrated analytics building blocks
Drupal 8 Statistics API
Drupal 8 Statistics API Concepts
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:
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...
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 '';� }� }�}
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...
// 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 '';� }� }�}
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:
Note: Nearly all of this is taken care of for you in a base class.
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 {}
Overriding StatMethod plugins
1. StatPluginMethodInterface::collect()
2. StatPluginMethodInterface::methodForm()
3. StatPluginMethodInterface::restrict()
4. StatPluginMethodInterface::manageData()
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...
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
Putting it all together
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>
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.
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.
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:
Goal: By Drupal 8.0 stable
Additional modules implementing the API:
Ways to contribute
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
Questions, discussion, resources
Drupal Projects:
https://drupal.org/project/sapi
https://drupal.org/project/better_statistics
Contact:
Twitter: iamEAP
Also everywhere else.