Who's this for

  • Laravel developers and burgeoning data fiends.

tl;dr

  • Tracking clicks with Laravel is suuuuper easy, and can provide a great foundation to begin building your own in-app analytics.

Over the past several months, Inquiri has been building a custom CMS to support a community of participants in a pharmaceutical study. Among other features, the platform offers these participants a steady stream of third-party articles. A user can view a title, excerpt, and preview image on our site, and then click through to view on the full article in its original context.

HIPAA concerns limited how we could use Google Analytics, so we needed to find another way to track a post's popularity.

The method below is geared toward blog posts, but can easily be adapted to profiles, product listings, or any other scenario where clicks need to be tracked within your app.

Step #1: Create Your Migration

The first thing we need to do is to create a new column within our posts table to hold the number of clicks:


Schema::table('posts', function($table){
    $table->integer('clicks');
});

Our only need here is to display the total number of post clicks, so we aren't storing any extra data about the click. If you need to do more with that data—say, chart clicks month over month, or track clicks per user per post—you will want to create a new table that looks more like this:


Schema::create('clicks', function (Blueprint $table){
    $table->increments('id');
    $table->integer('post_id')->unsigned();
    $table->integer('user_id')->unsigned();
    $table->timestamps();
}

Step #2: Create Your Route

We will need to point our AJAX to a specific URL that backs up to a function in our controller. So, in your routes.php file, add in:


Route::get('post/{id}/click', 'YourController@clickPost');

Step #3: Create Your Function

In your PostController.php file (or whichever controller makes sense in your application), we need to add the clickPost function that we referenced in our route. This function needs to accomplish two goals:

  • Accept an ID to find the post in question
  • Increment the value in the clicks column

Let's take a look:


/**
 * Increment post clicks
 *
 * @return \Illuminate\Http\Response
 */
public function clickPost($id)
{
    $post = Post::findorfail($id); // Find our post by ID.
    $post->increment('clicks'); // Increment the value in the clicks column.
    $post->update(); // Save our updated post.
}

Step #4: Modify Your View

We're so close: we have a route that points to a function to increment our new clicks column. Let's hop over to the client side and wrap this up.

In our view, we need to make two modifications. First, we need to add a data attribute that our Javascript can use to listen for click events. Then, add an attribute that contains the ID of our post. Our AJAX will pass this to the clickPost function:


@foreach ($posts as $post)
    
    
@endforeach

Mileage will probably vary on what your actual markup looks like, and that's totally fine. The two important elements here are the data-post-id attribute on our <article> element, and the .js-click-post class in our link.

Note: This example obviously makes use of a data attribute. Data attributes are fun and exciting, but do come with certain performance and accessibility limitations. Make sure you're aware of them—and how they could impact your project—before proceding.

Bonus: Want to exclude an admin role type for cleaner data? Use a ternary operator to conditionally show the .js-click-post class:



    {{ $post->title }}

Step #6: Write Your AJAX

The final step: our Javascript. First, we need to listen for a link click:


$( '.js-click-post' ).click(function(e) {
  e.preventDefault; // Prevent the default behavior of the  element.
  var post = $(this).closest('.post'); // Find the parent .post element
  var postId = post.attr('data-post-id'); // Get the post ID from our data attribute
  registerPostClick(postId); // Pass that ID to our function. 
});

Then, the AJAX itself:


function registerPostClick(postId) {
  $.ajaxSetup({
      headers: {
          'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
      }
  })
  $.ajax({
    type: 'post',
    dataType: 'JSON',
    url: '/post/' + postId + '/click',
    error: function (xhr, ajaxOptions, thrownError) {
           console.log(xhr.status);
           console.log(JSON.stringify(xhr.responseText));
       }
  });
}

We're taking our postID variable and dropping that into our URL structure. Our function hits that URL, and by extension, the controller, which increments our post count.

Step #7: Test It Out

Now that our pieces are in place, go ahead and give your post a click. If the post count increments, congratulations: you're now in the click tracking business.

If the post count does not increment, check your console for errors. If the AJAX function above fails for any reason, it will print both the status code and the corresponding response text. Those two pieces of information should help you quickly isolate and resolve any bugs that may arise.


This is part one of an extra-special two-part series: next week, we'll tackle how to register page views in a variety of contexts. Meanwhile, tell me about your click-tracking exploits—or how tired you think my Simpsons hero images are—on Twitter.